void DoSerialise(SerialiserType &ser, DynamicDescriptorCopy &el) { D3D12ResourceManager *rm = (D3D12ResourceManager *)ser.GetUserData(); SERIALISE_MEMBER(type); PortableHandle dst, src; if(ser.IsWriting()) { dst = ToPortableHandle(el.dst); src = ToPortableHandle(el.src); } ser.Serialise("dst", dst); ser.Serialise("src", src); if(ser.IsReading()) { if(rm) { el.dst = DescriptorFromPortableHandle(rm, dst); el.src = DescriptorFromPortableHandle(rm, src); } else { el.dst = NULL; el.src = NULL; } } }
void DoSerialiseViaResourceId(SerialiserType &ser, Interface *&el) { D3D12ResourceManager *rm = (D3D12ResourceManager *)ser.GetUserData(); ResourceId id; if(ser.IsWriting()) id = GetResID(el); DoSerialise(ser, id); if(ser.IsReading()) { if(id != ResourceId() && rm && rm->HasLiveResource(id)) el = rm->GetLiveAs<Interface>(id); else el = NULL; } }
void DoSerialise(SerialiserType &ser, D3D12_GPU_DESCRIPTOR_HANDLE &el) { D3D12ResourceManager *rm = (D3D12ResourceManager *)ser.GetUserData(); PortableHandle ph; if(ser.IsWriting()) ph = ToPortableHandle(el); DoSerialise(ser, ph); if(ser.IsReading()) { if(rm) el.ptr = (SIZE_T)DescriptorFromPortableHandle(rm, ph); else el.ptr = 0; } }
void DoSerialise(SerialiserType &ser, D3D12BufferLocation &el) { D3D12ResourceManager *rm = (D3D12ResourceManager *)ser.GetUserData(); ResourceId buffer; UINT64 offs = 0; if(ser.IsWriting()) WrappedID3D12Resource::GetResIDFromAddr(el.Location, buffer, offs); ser.Serialise("Buffer", buffer); ser.Serialise("Offset", offs); if(ser.IsReading()) { if(rm && buffer != ResourceId() && rm->HasLiveResource(buffer)) el.Location = rm->GetLiveAs<ID3D12Resource>(buffer)->GetGPUVirtualAddress() + offs; else el.Location = 0; } }
void SerialiseProgramBindings(SerialiserType &ser, CaptureState state, const GLHookSet &gl, GLuint prog) { std::vector<ProgramBinding> InputBindings; std::vector<ProgramBinding> OutputBindings; if(ser.IsWriting()) { char buf[128] = {}; for(int sigType = 0; sigType < 2; sigType++) { GLenum sigEnum = (sigType == 0 ? eGL_PROGRAM_INPUT : eGL_PROGRAM_OUTPUT); std::vector<ProgramBinding> &bindings = (sigType == 0 ? InputBindings : OutputBindings); int32_t NumAttributes = 0; gl.glGetProgramInterfaceiv(prog, sigEnum, eGL_ACTIVE_RESOURCES, (GLint *)&NumAttributes); bindings.reserve(NumAttributes); for(GLint i = 0; i < NumAttributes; i++) { gl.glGetProgramResourceName(prog, sigEnum, i, 128, NULL, buf); ProgramBinding bind; bind.Name = buf; if(sigType == 0) bind.Binding = gl.glGetAttribLocation(prog, buf); else bind.Binding = gl.glGetFragDataLocation(prog, buf); bindings.push_back(bind); } } } SERIALISE_ELEMENT(InputBindings); SERIALISE_ELEMENT(OutputBindings); if(ser.IsReading() && IsReplayMode(state)) { for(int sigType = 0; sigType < 2; sigType++) { const std::vector<ProgramBinding> &bindings = (sigType == 0 ? InputBindings : OutputBindings); uint64_t used = 0; for(const ProgramBinding &bind : bindings) { if(bind.Binding >= 0) { uint64_t mask = 1ULL << bind.Binding; if(used & mask) { RDCWARN("Multiple %s items bound to location %d, ignoring %s", sigType == 0 ? "attrib" : "fragdata", bind.Binding, bind.Name.c_str()); continue; } used |= mask; if(!strncmp("gl_", bind.Name.c_str(), 3)) continue; // GL_INVALID_OPERATION if name starts with reserved gl_ prefix (for both // glBindAttribLocation and glBindFragDataLocation) if(sigType == 0) { gl.glBindAttribLocation(prog, (GLuint)bind.Binding, bind.Name.c_str()); } else { if(gl.glBindFragDataLocation) { gl.glBindFragDataLocation(prog, (GLuint)bind.Binding, bind.Name.c_str()); } else { // glBindFragDataLocation is not core GLES, but it is in GL_EXT_blend_func_extended // TODO what to do if that extension is not supported RDCERR("glBindFragDataLocation is not supported!"); } } } } } } }
bool WrappedOpenGL::Serialise_wglDXLockObjectsNV(SerialiserType &ser, GLResource Resource) { SERIALISE_ELEMENT(Resource); SERIALISE_ELEMENT_LOCAL(textype, Resource.Namespace == eResBuffer ? eGL_NONE : m_Textures[GetResourceManager()->GetID(Resource)].curType) .Hidden(); const GLHookSet &gl = m_Real; // buffer contents are easier to save if(textype == eGL_NONE) { byte *Contents = NULL; uint32_t length = 1; // while writing, fetch the buffer's size and contents if(ser.IsWriting()) { gl.glGetNamedBufferParameterivEXT(Resource.name, eGL_BUFFER_SIZE, (GLint *)&length); Contents = new byte[length]; GLuint oldbuf = 0; gl.glGetIntegerv(eGL_COPY_READ_BUFFER_BINDING, (GLint *)&oldbuf); gl.glBindBuffer(eGL_COPY_READ_BUFFER, Resource.name); gl.glGetBufferSubData(eGL_COPY_READ_BUFFER, 0, (GLsizeiptr)length, Contents); gl.glBindBuffer(eGL_COPY_READ_BUFFER, oldbuf); } SERIALISE_ELEMENT_ARRAY(Contents, length); SERIALISE_CHECK_READ_ERRORS(); // restore on replay if(IsReplayingAndReading()) { uint32_t liveLength = 1; gl.glGetNamedBufferParameterivEXT(Resource.name, eGL_BUFFER_SIZE, (GLint *)&liveLength); gl.glNamedBufferSubData(Resource.name, 0, (GLsizeiptr)RDCMIN(length, liveLength), Contents); } } else { GLuint ppb = 0, pub = 0; PixelPackState pack; PixelUnpackState unpack; // save and restore pixel pack/unpack state. We only need one or the other but for clarity we // push and pop both always. if(ser.IsWriting() || !IsStructuredExporting(m_State)) { gl.glGetIntegerv(eGL_PIXEL_PACK_BUFFER_BINDING, (GLint *)&ppb); gl.glGetIntegerv(eGL_PIXEL_UNPACK_BUFFER_BINDING, (GLint *)&pub); gl.glBindBuffer(eGL_PIXEL_PACK_BUFFER, 0); gl.glBindBuffer(eGL_PIXEL_UNPACK_BUFFER, 0); pack.Fetch(&gl, false); unpack.Fetch(&gl, false); ResetPixelPackState(gl, false, 1); ResetPixelUnpackState(gl, false, 1); } TextureData &details = m_Textures[GetResourceManager()->GetID(Resource)]; GLuint tex = Resource.name; // serialise the metadata for convenience SERIALISE_ELEMENT_LOCAL(internalFormat, details.internalFormat).Hidden(); SERIALISE_ELEMENT_LOCAL(width, details.width).Hidden(); SERIALISE_ELEMENT_LOCAL(height, details.height).Hidden(); SERIALISE_ELEMENT_LOCAL(depth, details.depth).Hidden(); RDCASSERT(internalFormat == details.internalFormat, internalFormat, details.internalFormat); RDCASSERT(width == details.width, width, details.width); RDCASSERT(height == details.height, height, details.height); RDCASSERT(depth == details.depth, depth, details.depth); GLenum fmt = GetBaseFormat(internalFormat); GLenum type = GetDataType(internalFormat); GLint dim = details.dimension; uint32_t size = (uint32_t)GetByteSize(width, height, depth, fmt, type); int mips = 0; if(IsReplayingAndReading()) mips = GetNumMips(gl, textype, tex, width, height, depth); byte *scratchBuf = NULL; // on read and write, we allocate a single buffer big enough for all mips and re-use it // to avoid repeated new/free. scratchBuf = AllocAlignedBuffer(size); GLuint prevtex = 0; if(!IsStructuredExporting(m_State)) { gl.glGetIntegerv(TextureBinding(details.curType), (GLint *)&prevtex); gl.glBindTexture(textype, tex); } for(int i = 0; i < mips; i++) { int w = RDCMAX(details.width >> i, 1); int h = RDCMAX(details.height >> i, 1); int d = RDCMAX(details.depth >> i, 1); if(textype == eGL_TEXTURE_CUBE_MAP_ARRAY || textype == eGL_TEXTURE_1D_ARRAY || textype == eGL_TEXTURE_2D_ARRAY) d = details.depth; size = (uint32_t)GetByteSize(w, h, d, fmt, type); GLenum targets[] = { eGL_TEXTURE_CUBE_MAP_POSITIVE_X, eGL_TEXTURE_CUBE_MAP_NEGATIVE_X, eGL_TEXTURE_CUBE_MAP_POSITIVE_Y, eGL_TEXTURE_CUBE_MAP_NEGATIVE_Y, eGL_TEXTURE_CUBE_MAP_POSITIVE_Z, eGL_TEXTURE_CUBE_MAP_NEGATIVE_Z, }; int count = ARRAY_COUNT(targets); if(textype != eGL_TEXTURE_CUBE_MAP) { targets[0] = textype; count = 1; } for(int trg = 0; trg < count; trg++) { if(ser.IsWriting()) { // we avoid glGetTextureImageEXT as it seems buggy for cubemap faces gl.glGetTexImage(targets[trg], i, fmt, type, scratchBuf); } // serialise without allocating memory as we already have our scratch buf sized. ser.Serialise("SubresourceContents", scratchBuf, size, SerialiserFlags::NoFlags); if(IsReplayingAndReading() && !ser.IsErrored()) { if(dim == 1) gl.glTextureSubImage1DEXT(tex, targets[trg], i, 0, w, fmt, type, scratchBuf); else if(dim == 2) gl.glTextureSubImage2DEXT(tex, targets[trg], i, 0, 0, w, h, fmt, type, scratchBuf); else if(dim == 3) gl.glTextureSubImage3DEXT(tex, targets[trg], i, 0, 0, 0, w, h, d, fmt, type, scratchBuf); } } } FreeAlignedBuffer(scratchBuf); // restore pixel (un)packing state if(ser.IsWriting() || !IsStructuredExporting(m_State)) { gl.glBindBuffer(eGL_PIXEL_PACK_BUFFER, ppb); gl.glBindBuffer(eGL_PIXEL_UNPACK_BUFFER, pub); pack.Apply(&gl, false); unpack.Apply(&gl, false); } if(!IsStructuredExporting(m_State)) gl.glBindTexture(textype, prevtex); SERIALISE_CHECK_READ_ERRORS(); } return true; }
bool WrappedOpenGL::Serialise_wglDXRegisterObjectNV(SerialiserType &ser, GLResource Resource, GLenum type, void *dxObject) { SERIALISE_ELEMENT(Resource); GLenum internalFormat = eGL_NONE; uint32_t width = 0, height = 0, depth = 0, mips = 0, layers = 0, samples = 0; if(ser.IsWriting()) { ResourceFormat format; #if ENABLED(RDOC_WIN32) && ENABLED(RENDERDOC_DX_GL_INTEROP) GetDXTextureProperties(dxObject, format, width, height, depth, mips, layers, samples); if(type != eGL_NONE) internalFormat = MakeGLFormat(format); #else RDCERR("Should never happen - cannot serialise wglDXRegisterObjectNV, interop is disabled"); #endif } SERIALISE_ELEMENT(type); SERIALISE_ELEMENT(internalFormat); SERIALISE_ELEMENT(width); SERIALISE_ELEMENT(height); SERIALISE_ELEMENT(depth); SERIALISE_ELEMENT(mips); SERIALISE_ELEMENT(layers); SERIALISE_ELEMENT(samples); SERIALISE_CHECK_READ_ERRORS(); if(IsReplayingAndReading()) { GLuint name = Resource.name; switch(type) { case eGL_NONE: case eGL_TEXTURE_BUFFER: { m_Real.glNamedBufferDataEXT(name, width, NULL, eGL_STATIC_DRAW); break; } case eGL_TEXTURE_1D: m_Real.glTextureStorage1DEXT(name, type, mips, internalFormat, width); break; case eGL_TEXTURE_1D_ARRAY: m_Real.glTextureStorage2DEXT(name, type, mips, internalFormat, width, layers); break; // treat renderbuffers and texture rects as tex2D just to make things easier case eGL_RENDERBUFFER: case eGL_TEXTURE_RECTANGLE: case eGL_TEXTURE_2D: case eGL_TEXTURE_CUBE_MAP: m_Real.glTextureStorage2DEXT(name, type, mips, internalFormat, width, height); break; case eGL_TEXTURE_2D_ARRAY: case eGL_TEXTURE_CUBE_MAP_ARRAY: m_Real.glTextureStorage3DEXT(name, type, mips, internalFormat, width, height, layers); break; case eGL_TEXTURE_2D_MULTISAMPLE: m_Real.glTextureStorage2DMultisampleEXT(name, type, samples, internalFormat, width, height, GL_TRUE); break; case eGL_TEXTURE_2D_MULTISAMPLE_ARRAY: m_Real.glTextureStorage3DMultisampleEXT(name, type, samples, internalFormat, width, height, layers, GL_TRUE); break; case eGL_TEXTURE_3D: m_Real.glTextureStorage3DEXT(name, type, mips, internalFormat, width, height, depth); break; default: RDCERR("Unexpected type of interop texture: %s", ToStr(type).c_str()); break; } if(type != eGL_NONE) { ResourceId liveId = GetResourceManager()->GetID(Resource); m_Textures[liveId].curType = type; m_Textures[liveId].width = width; m_Textures[liveId].height = height; m_Textures[liveId].depth = RDCMAX(depth, samples); m_Textures[liveId].samples = samples; m_Textures[liveId].dimension = 2; if(type == eGL_TEXTURE_1D || type == eGL_TEXTURE_1D_ARRAY) m_Textures[liveId].dimension = 1; else if(type == eGL_TEXTURE_3D) m_Textures[liveId].dimension = 3; m_Textures[liveId].internalFormat = internalFormat; } AddResourceInitChunk(Resource); } return true; }
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; }