Ejemplo n.º 1
0
void WrappedOpenGL::glCompileShaderIncludeARB(GLuint shader, GLsizei count,
                                              const GLchar *const *path, const GLint *length)
{
  m_Real.glCompileShaderIncludeARB(shader, count, path, length);

  if(m_State >= WRITING)
  {
    GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ShaderRes(GetCtx(), shader));
    RDCASSERT(record);
    {
      SCOPED_SERIALISE_CONTEXT(COMPILESHADERINCLUDE);
      Serialise_glCompileShaderIncludeARB(shader, count, path, length);

      record->AddChunk(scope.Get());
    }
  }
  else
  {
    ResourceId id = GetResourceManager()->GetID(ShaderRes(GetCtx(), shader));

    auto &shadDetails = m_Shaders[id];

    shadDetails.includepaths.clear();
    shadDetails.includepaths.reserve(count);

    for(int32_t i = 0; i < count; i++)
      shadDetails.includepaths.push_back(path[i]);

    shadDetails.Compile(*this);
  }
}
Ejemplo n.º 2
0
void WrappedOpenGL::glAttachShader(GLuint program, GLuint shader)
{
  m_Real.glAttachShader(program, shader);

  if(m_State >= WRITING && program != 0 && shader != 0)
  {
    GLResourceRecord *progRecord =
        GetResourceManager()->GetResourceRecord(ProgramRes(GetCtx(), program));
    GLResourceRecord *shadRecord =
        GetResourceManager()->GetResourceRecord(ShaderRes(GetCtx(), shader));
    RDCASSERT(progRecord && shadRecord);
    if(progRecord && shadRecord)
    {
      SCOPED_SERIALISE_CONTEXT(ATTACHSHADER);
      Serialise_glAttachShader(program, shader);

      progRecord->AddParent(shadRecord);
      progRecord->AddChunk(scope.Get());
    }
  }
  else
  {
    ResourceId progid = GetResourceManager()->GetID(ProgramRes(GetCtx(), program));
    ResourceId shadid = GetResourceManager()->GetID(ShaderRes(GetCtx(), shader));
    m_Programs[progid].shaders.push_back(shadid);
  }
}
Ejemplo n.º 3
0
void WrappedOpenGL::glShaderSource(GLuint shader, GLsizei count, const GLchar *const *string,
                                   const GLint *length)
{
  m_Real.glShaderSource(shader, count, string, length);

  if(m_State >= WRITING)
  {
    GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ShaderRes(GetCtx(), shader));
    RDCASSERT(record);
    {
      SCOPED_SERIALISE_CONTEXT(SHADERSOURCE);
      Serialise_glShaderSource(shader, count, string, length);

      record->AddChunk(scope.Get());
    }
  }
  else
  {
    ResourceId id = GetResourceManager()->GetID(ShaderRes(GetCtx(), shader));
    m_Shaders[id].sources.clear();
    m_Shaders[id].sources.reserve(count);

    for(GLsizei i = 0; i < count; i++)
      m_Shaders[id].sources.push_back(string[i]);
  }
}
Ejemplo n.º 4
0
GLuint WrappedOpenGL::glCreateShader(GLenum type)
{
  GLuint real = m_Real.glCreateShader(type);

  GLResource res = ShaderRes(GetCtx(), real);
  ResourceId id = GetResourceManager()->RegisterResource(res);

  if(m_State >= WRITING)
  {
    Chunk *chunk = NULL;

    {
      SCOPED_SERIALISE_CONTEXT(CREATE_SHADER);
      Serialise_glCreateShader(real, type);

      chunk = scope.Get();
    }

    GLResourceRecord *record = GetResourceManager()->AddResourceRecord(id);
    RDCASSERT(record);

    record->AddChunk(chunk);
  }
  else
  {
    GetResourceManager()->AddLiveResource(id, res);

    m_Shaders[id].type = type;
  }

  return real;
}
Ejemplo n.º 5
0
bool WrappedOpenGL::Serialise_glDetachShader(GLuint program, GLuint shader)
{
  SERIALISE_ELEMENT(ResourceId, progid, GetResourceManager()->GetID(ProgramRes(GetCtx(), program)));
  SERIALISE_ELEMENT(ResourceId, shadid, GetResourceManager()->GetID(ShaderRes(GetCtx(), shader)));

  if(m_State == READING)
  {
    ResourceId liveProgId = GetResourceManager()->GetLiveID(progid);
    ResourceId liveShadId = GetResourceManager()->GetLiveID(shadid);

    if(!m_Programs[liveProgId].linked)
    {
      for(auto it = m_Programs[liveProgId].shaders.begin();
          it != m_Programs[liveProgId].shaders.end(); ++it)
      {
        if(*it == liveShadId)
        {
          m_Programs[liveProgId].shaders.erase(it);
          break;
        }
      }
    }

    m_Real.glDetachShader(GetResourceManager()->GetLiveResource(progid).name,
                          GetResourceManager()->GetLiveResource(shadid).name);
  }

  return true;
}
Ejemplo n.º 6
0
bool WrappedOpenGL::Serialise_glCreateShader(GLuint shader, GLenum type)
{
  SERIALISE_ELEMENT(GLenum, Type, type);
  SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(ShaderRes(GetCtx(), shader)));

  if(m_State == READING)
  {
    GLuint real = m_Real.glCreateShader(Type);

    GLResource res = ShaderRes(GetCtx(), real);

    ResourceId liveId = GetResourceManager()->RegisterResource(res);

    m_Shaders[liveId].type = Type;

    GetResourceManager()->AddLiveResource(id, res);
  }

  return true;
}
Ejemplo n.º 7
0
void WrappedOpenGL::glCompileShader(GLuint shader)
{
  m_Real.glCompileShader(shader);

  if(m_State >= WRITING)
  {
    GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ShaderRes(GetCtx(), shader));
    RDCASSERT(record);
    {
      SCOPED_SERIALISE_CONTEXT(COMPILESHADER);
      Serialise_glCompileShader(shader);

      record->AddChunk(scope.Get());
    }
  }
  else
  {
    m_Shaders[GetResourceManager()->GetID(ShaderRes(GetCtx(), shader))].Compile(*this);
  }
}
Ejemplo n.º 8
0
void WrappedOpenGL::glDeleteShader(GLuint shader)
{
  m_Real.glDeleteShader(shader);

  GLResource res = ShaderRes(GetCtx(), shader);
  if(GetResourceManager()->HasCurrentResource(res))
  {
    if(GetResourceManager()->HasResourceRecord(res))
      GetResourceManager()->GetResourceRecord(res)->Delete(GetResourceManager());
    GetResourceManager()->UnregisterResource(res);
  }
}
Ejemplo n.º 9
0
void WrappedOpenGL::glDetachShader(GLuint program, GLuint shader)
{
  m_Real.glDetachShader(program, shader);

  // check that shader still exists, it might have been deleted. If it has, it's not too important
  // that we detach the shader (only important if the program will attach it elsewhere).
  if(m_State >= WRITING && program != 0 && shader != 0 &&
     GetResourceManager()->HasCurrentResource(ShaderRes(GetCtx(), shader)))
  {
    GLResourceRecord *progRecord =
        GetResourceManager()->GetResourceRecord(ProgramRes(GetCtx(), program));
    RDCASSERT(progRecord);
    {
      SCOPED_SERIALISE_CONTEXT(DETACHSHADER);
      Serialise_glDetachShader(program, shader);

      progRecord->AddChunk(scope.Get());
    }
  }
  else
  {
    ResourceId progid = GetResourceManager()->GetID(ProgramRes(GetCtx(), program));
    ResourceId shadid = GetResourceManager()->GetID(ShaderRes(GetCtx(), shader));

    if(!m_Programs[progid].linked)
    {
      for(auto it = m_Programs[progid].shaders.begin(); it != m_Programs[progid].shaders.end(); ++it)
      {
        if(*it == shadid)
        {
          m_Programs[progid].shaders.erase(it);
          break;
        }
      }
    }
  }
}
Ejemplo n.º 10
0
bool WrappedOpenGL::Serialise_glCompileShaderIncludeARB(GLuint shader, GLsizei count,
                                                        const GLchar *const *path,
                                                        const GLint *length)
{
  SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(ShaderRes(GetCtx(), shader)));
  SERIALISE_ELEMENT(int32_t, Count, count);

  vector<string> paths;

  for(int32_t i = 0; i < Count; i++)
  {
    string s;
    if(path && path[i])
      s = (length && length[i] > 0) ? string(path[i], path[i] + length[i]) : string(path[i]);

    m_pSerialiser->SerialiseString("path", s);

    if(m_State == READING)
      paths.push_back(s);
  }

  if(m_State == READING)
  {
    size_t numStrings = paths.size();

    const char **pathstrings = new const char *[numStrings];
    for(size_t i = 0; i < numStrings; i++)
      pathstrings[i] = paths[i].c_str();

    ResourceId liveId = GetResourceManager()->GetLiveID(id);

    auto &shadDetails = m_Shaders[liveId];

    shadDetails.includepaths.clear();
    shadDetails.includepaths.reserve(Count);

    for(int32_t i = 0; i < Count; i++)
      shadDetails.includepaths.push_back(pathstrings[i]);

    shadDetails.Compile(*this);

    m_Real.glCompileShaderIncludeARB(GetResourceManager()->GetLiveResource(id).name, Count,
                                     pathstrings, NULL);

    delete[] pathstrings;
  }

  return true;
}
Ejemplo n.º 11
0
bool WrappedOpenGL::Serialise_glCompileShader(GLuint shader)
{
  SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(ShaderRes(GetCtx(), shader)));

  if(m_State == READING)
  {
    ResourceId liveId = GetResourceManager()->GetLiveID(id);

    m_Shaders[liveId].Compile(*this);

    m_Real.glCompileShader(GetResourceManager()->GetLiveResource(id).name);
  }

  return true;
}
Ejemplo n.º 12
0
void WrappedOpenGL::glCompileShaderIncludeARB(GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length)
{
	m_Real.glCompileShaderIncludeARB(shader, count, path, length);
	
	if(m_State >= WRITING)
	{
		GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ShaderRes(GetCtx(), shader));
		RDCASSERT(record);
		{
			SCOPED_SERIALISE_CONTEXT(COMPILESHADERINCLUDE);
			Serialise_glCompileShaderIncludeARB(shader, count, path, length);

			record->AddChunk(scope.Get());
		}
	}
}
Ejemplo n.º 13
0
void WrappedOpenGL::glShaderSource(GLuint shader, GLsizei count, const GLchar* const *string, const GLint *length)
{
	m_Real.glShaderSource(shader, count, string, length);
	
	if(m_State >= WRITING)
	{
		GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ShaderRes(GetCtx(), shader));
		RDCASSERT(record);
		{
			SCOPED_SERIALISE_CONTEXT(SHADERSOURCE);
			Serialise_glShaderSource(shader, count, string, length);

			record->AddChunk(scope.Get());
		}
	}
}
Ejemplo n.º 14
0
bool WrappedOpenGL::Serialise_glAttachShader(GLuint program, GLuint shader)
{
  SERIALISE_ELEMENT(ResourceId, progid, GetResourceManager()->GetID(ProgramRes(GetCtx(), program)));
  SERIALISE_ELEMENT(ResourceId, shadid, GetResourceManager()->GetID(ShaderRes(GetCtx(), shader)));

  if(m_State == READING)
  {
    ResourceId liveProgId = GetResourceManager()->GetLiveID(progid);
    ResourceId liveShadId = GetResourceManager()->GetLiveID(shadid);

    m_Programs[liveProgId].shaders.push_back(liveShadId);

    m_Real.glAttachShader(GetResourceManager()->GetLiveResource(progid).name,
                          GetResourceManager()->GetLiveResource(shadid).name);
  }

  return true;
}
Ejemplo n.º 15
0
bool WrappedOpenGL::Serialise_glShaderSource(GLuint shader, GLsizei count,
                                             const GLchar *const *source, const GLint *length)
{
  SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(ShaderRes(GetCtx(), shader)));
  SERIALISE_ELEMENT(uint32_t, Count, count);

  vector<string> srcs;

  for(uint32_t i = 0; i < Count; i++)
  {
    string s;
    if(source && source[i])
      s = (length && length[i] > 0) ? string(source[i], source[i] + length[i]) : string(source[i]);

    m_pSerialiser->SerialiseString("source", s);

    if(m_State == READING)
      srcs.push_back(s);
  }

  if(m_State == READING)
  {
    size_t numStrings = srcs.size();

    const char **strings = new const char *[numStrings];
    for(size_t i = 0; i < numStrings; i++)
      strings[i] = srcs[i].c_str();

    ResourceId liveId = GetResourceManager()->GetLiveID(id);

    m_Shaders[liveId].sources.clear();
    m_Shaders[liveId].sources.reserve(Count);

    for(uint32_t i = 0; i < Count; i++)
      m_Shaders[liveId].sources.push_back(strings[i]);

    m_Real.glShaderSource(GetResourceManager()->GetLiveResource(id).name, Count, strings, NULL);

    delete[] strings;
  }

  return true;
}
Ejemplo n.º 16
0
bool WrappedOpenGL::Serialise_glCompileShader(GLuint shader)
{
	SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(ShaderRes(GetCtx(), shader)));
	
	if(m_State == READING)
	{
		ResourceId liveId = GetResourceManager()->GetLiveID(id);

		auto &shadDetails = m_Shaders[liveId];

		bool pointSizeUsed = false, clipDistanceUsed = false;
		if(shadDetails.type == eGL_VERTEX_SHADER) CheckVertexOutputUses(shadDetails.sources, pointSizeUsed, clipDistanceUsed);

		GLuint sepProg = MakeSeparableShaderProgram(m_Real, shadDetails.type, shadDetails.sources, NULL);

		if(sepProg == 0)
		{
			RDCERR("Couldn't make separable program for shader via patching - functionality will be broken.");
		}
		else
		{
			shadDetails.prog = sepProg;
			MakeShaderReflection(m_Real, shadDetails.type, sepProg, shadDetails.reflection, pointSizeUsed, clipDistanceUsed);

			string s = CompileSPIRV(SPIRVShaderStage(ShaderIdx(shadDetails.type)), shadDetails.sources, shadDetails.spirv);
			if(!shadDetails.spirv.empty())
				DisassembleSPIRV(SPIRVShaderStage(ShaderIdx(shadDetails.type)), shadDetails.spirv, s);

			shadDetails.reflection.Disassembly = s;

			create_array_uninit(shadDetails.reflection.DebugInfo.files, shadDetails.sources.size());
			for(size_t i=0; i < shadDetails.sources.size(); i++)
			{
				shadDetails.reflection.DebugInfo.files[i].first = StringFormat::Fmt("source%u.glsl", (uint32_t)i);
				shadDetails.reflection.DebugInfo.files[i].second = shadDetails.sources[i];
			}
		}

		m_Real.glCompileShader(GetResourceManager()->GetLiveResource(id).name);
	}

	return true;
}
Ejemplo n.º 17
0
bool WrappedOpenGL::Serialise_glObjectLabel(SerialiserType &ser, GLenum identifier, GLuint name,
                                            GLsizei length, const GLchar *label)
{
  GLResource Resource;
  std::string Label;

  if(ser.IsWriting())
  {
    // we share implementations between KHR_debug and EXT_debug_label, however KHR_debug follows the
    // pattern elsewhere (e.g. in glShaderSource) of a length of -1 meaning indeterminate
    // NULL-terminated length, but EXT_debug_label takes length of 0 to mean that.
    GLsizei realLength = length;
    if(gl_CurChunk == GLChunk::glLabelObjectEXT && length == 0)
      realLength = -1;

    // if length is negative (after above twiddling), it's taken from strlen and the label must be
    // NULL-terminated
    if(realLength < 0)
      realLength = label ? (GLsizei)strlen(label) : 0;

    if(realLength == 0 || label == NULL)
      Label = "";
    else
      Label = std::string(label, label + realLength);

    switch(identifier)
    {
      case eGL_TEXTURE: Resource = TextureRes(GetCtx(), name); break;
      case eGL_BUFFER_OBJECT_EXT:
      case eGL_BUFFER: Resource = BufferRes(GetCtx(), name); break;
      case eGL_PROGRAM_OBJECT_EXT:
      case eGL_PROGRAM: Resource = ProgramRes(GetCtx(), name); break;
      case eGL_PROGRAM_PIPELINE_OBJECT_EXT:
      case eGL_PROGRAM_PIPELINE: Resource = ProgramPipeRes(GetCtx(), name); break;
      case eGL_VERTEX_ARRAY_OBJECT_EXT:
      case eGL_VERTEX_ARRAY: Resource = VertexArrayRes(GetCtx(), name); break;
      case eGL_SHADER_OBJECT_EXT:
      case eGL_SHADER: Resource = ShaderRes(GetCtx(), name); break;
      case eGL_QUERY_OBJECT_EXT:
      case eGL_QUERY: Resource = QueryRes(GetCtx(), name); break;
      case eGL_TRANSFORM_FEEDBACK: Resource = FeedbackRes(GetCtx(), name); break;
      case eGL_SAMPLER: Resource = SamplerRes(GetCtx(), name); break;
      case eGL_RENDERBUFFER: Resource = RenderbufferRes(GetCtx(), name); break;
      case eGL_FRAMEBUFFER: Resource = FramebufferRes(GetCtx(), name); break;
      default: RDCERR("Unhandled namespace in glObjectLabel");
    }
  }

  SERIALISE_ELEMENT(Resource);
  SERIALISE_ELEMENT(length);
  SERIALISE_ELEMENT(Label);

  SERIALISE_CHECK_READ_ERRORS();

  if(IsReplayingAndReading() && Resource.name)
  {
    ResourceId origId = GetResourceManager()->GetOriginalID(GetResourceManager()->GetID(Resource));

    GetResourceManager()->SetName(origId, Label);

    ResourceDescription &descr = GetReplay()->GetResourceDesc(origId);
    descr.SetCustomName(Label);
    AddResourceCurChunk(descr);
  }

  return true;
}
Ejemplo n.º 18
0
bool WrappedOpenGL::Serialise_glCompileShaderIncludeARB(GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length)
{
	SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(ShaderRes(GetCtx(), shader)));
	SERIALISE_ELEMENT(int32_t, Count, count);

	vector<string> paths;

	for(int32_t i=0; i < Count; i++)
	{
		string s;
		if(path && path[i])
			s = length > 0 ? string(path[i], path[i] + length[i]) : string(path[i]);
		
		m_pSerialiser->SerialiseString("path", s);

		if(m_State == READING)
			paths.push_back(s);
	}
	
	if(m_State == READING)
	{
		size_t numStrings = paths.size();

		const char **pathstrings = new const char*[numStrings];
		for(size_t i=0; i < numStrings; i++)
			pathstrings[i] = paths[i].c_str();

		ResourceId liveId = GetResourceManager()->GetLiveID(id);

		auto &shadDetails = m_Shaders[liveId];
		
		shadDetails.includepaths.clear();
		shadDetails.includepaths.reserve(Count);

		for(int32_t i=0; i < Count; i++)
			shadDetails.includepaths.push_back(pathstrings[i]);

		bool pointSizeUsed = false, clipDistanceUsed = false;
		if(shadDetails.type == eGL_VERTEX_SHADER) CheckVertexOutputUses(shadDetails.sources, pointSizeUsed, clipDistanceUsed);

		GLuint sepProg = MakeSeparableShaderProgram(m_Real, shadDetails.type, shadDetails.sources, &paths);

		if(sepProg == 0)
		{
			RDCERR("Couldn't make separable program for shader via patching - functionality will be broken.");
		}
		else
		{
			shadDetails.prog = sepProg;
			MakeShaderReflection(m_Real, shadDetails.type, sepProg, shadDetails.reflection, pointSizeUsed, clipDistanceUsed);
			
			string s = CompileSPIRV(SPIRVShaderStage(ShaderIdx(shadDetails.type)), shadDetails.sources, shadDetails.spirv);
			if(!shadDetails.spirv.empty())
				DisassembleSPIRV(SPIRVShaderStage(ShaderIdx(shadDetails.type)), shadDetails.spirv, s);

			shadDetails.reflection.Disassembly = s;

			create_array_uninit(shadDetails.reflection.DebugInfo.files, shadDetails.sources.size());
			for(size_t i=0; i < shadDetails.sources.size(); i++)
			{
				shadDetails.reflection.DebugInfo.files[i].first = StringFormat::Fmt("source%u.glsl", (uint32_t)i);
				shadDetails.reflection.DebugInfo.files[i].second = shadDetails.sources[i];
			}
		}

		m_Real.glCompileShaderIncludeARB(GetResourceManager()->GetLiveResource(id).name, Count, pathstrings, NULL);
		
		delete[] pathstrings;
	}

	return true;
}
Ejemplo n.º 19
0
bool WrappedOpenGL::Serialise_glObjectLabel(GLenum identifier, GLuint name, GLsizei length,
                                            const GLchar *label)
{
  ResourceId liveid;

  bool extvariant = false;

  string Label;
  if(m_State >= WRITING)
  {
    if(length == 0)
      Label = "";
    else
      Label = string(label, label + (length > 0 ? length : strlen(label)));

    switch(identifier)
    {
      case eGL_TEXTURE: liveid = GetResourceManager()->GetID(TextureRes(GetCtx(), name)); break;
      case eGL_BUFFER_OBJECT_EXT: extvariant = true;
      case eGL_BUFFER: liveid = GetResourceManager()->GetID(BufferRes(GetCtx(), name)); break;
      case eGL_PROGRAM_OBJECT_EXT: extvariant = true;
      case eGL_PROGRAM: liveid = GetResourceManager()->GetID(ProgramRes(GetCtx(), name)); break;
      case eGL_PROGRAM_PIPELINE_OBJECT_EXT: extvariant = true;
      case eGL_PROGRAM_PIPELINE:
        liveid = GetResourceManager()->GetID(ProgramPipeRes(GetCtx(), name));
        break;
      case eGL_VERTEX_ARRAY_OBJECT_EXT: extvariant = true;
      case eGL_VERTEX_ARRAY:
        liveid = GetResourceManager()->GetID(VertexArrayRes(GetCtx(), name));
        break;
      case eGL_SHADER_OBJECT_EXT: extvariant = true;
      case eGL_SHADER: liveid = GetResourceManager()->GetID(ShaderRes(GetCtx(), name)); break;
      case eGL_QUERY_OBJECT_EXT: extvariant = true;
      case eGL_QUERY: liveid = GetResourceManager()->GetID(QueryRes(GetCtx(), name)); break;
      case eGL_TRANSFORM_FEEDBACK:
        liveid = GetResourceManager()->GetID(FeedbackRes(GetCtx(), name));
        break;
      case eGL_SAMPLER: liveid = GetResourceManager()->GetID(SamplerRes(GetCtx(), name)); break;
      case eGL_RENDERBUFFER:
        liveid = GetResourceManager()->GetID(RenderbufferRes(GetCtx(), name));
        break;
      case eGL_FRAMEBUFFER:
        liveid = GetResourceManager()->GetID(FramebufferRes(GetCtx(), name));
        break;
      default: RDCERR("Unhandled namespace in glObjectLabel");
    }
  }

  SERIALISE_ELEMENT(GLenum, Identifier, identifier);
  SERIALISE_ELEMENT(ResourceId, id, liveid);
  SERIALISE_ELEMENT(uint32_t, Length, length);
  SERIALISE_ELEMENT(bool, HasLabel, label != NULL);

  m_pSerialiser->SerialiseString("label", Label);

  if(m_State == READING && GetResourceManager()->HasLiveResource(id))
  {
    GLResource res = GetResourceManager()->GetLiveResource(id);

    if(extvariant && m_Real.glLabelObjectEXT)
      m_Real.glLabelObjectEXT(Identifier, res.name, Length, HasLabel ? Label.c_str() : NULL);
    else
      m_Real.glObjectLabel(Identifier, res.name, Length, HasLabel ? Label.c_str() : NULL);
  }

  return true;
}
Ejemplo n.º 20
0
void WrappedOpenGL::glDetachShader(GLuint program, GLuint shader)
{
	m_Real.glDetachShader(program, shader);
	
	// check that shader still exists, it might have been deleted. If it has, it's not too important
	// that we detach the shader (only important if the program will attach it elsewhere).
	if(m_State >= WRITING && program != 0 && shader != 0 && GetResourceManager()->HasCurrentResource(ShaderRes(GetCtx(), shader)))
	{
		GLResourceRecord *progRecord = GetResourceManager()->GetResourceRecord(ProgramRes(GetCtx(), program));
		RDCASSERT(progRecord);
		{
			SCOPED_SERIALISE_CONTEXT(DETACHSHADER);
			Serialise_glDetachShader(program, shader);

			progRecord->AddChunk(scope.Get());
		}
	}
}