void GLReplay::CreateOutputWindowBackbuffer(OutputWindow &outwin) { if(m_pDriver == NULL) return; MakeCurrentReplayContext(m_DebugCtx); WrappedOpenGL &gl = *m_pDriver; // create fake backbuffer for this output window. // We'll make an FBO for this backbuffer on the replay context, so we can // use the replay context to do the hard work of rendering to it, then just // blit across to the real default framebuffer on the output window context gl.glGenFramebuffers(1, &outwin.BlitData.windowFBO); gl.glBindFramebuffer(eGL_FRAMEBUFFER, outwin.BlitData.windowFBO); gl.glGenTextures(1, &outwin.BlitData.backbuffer); gl.glBindTexture(eGL_TEXTURE_2D, outwin.BlitData.backbuffer); gl.glTexStorage2D(eGL_TEXTURE_2D, 1, eGL_RGB8, outwin.width, outwin.height); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MIN_FILTER, eGL_NEAREST); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAG_FILTER, eGL_NEAREST); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_WRAP_S, eGL_CLAMP_TO_EDGE); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_WRAP_T, eGL_CLAMP_TO_EDGE); gl.glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, outwin.BlitData.backbuffer, 0); outwin.BlitData.replayFBO = 0; }
bool GLReplay::CheckResizeOutputWindow(uint64_t id) { if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end()) return false; OutputWindow &outw = m_OutputWindows[id]; if(outw.wnd == 0) return false; int32_t w, h; GetOutputWindowDimensions(id, w, h); if(w != outw.width || h != outw.height) { outw.width = w; outw.height = h; MakeCurrentReplayContext(m_DebugCtx); WrappedOpenGL &gl = *m_pDriver; gl.glDeleteTextures(1, &outw.BlitData.backbuffer); gl.glDeleteFramebuffers(1, &outw.BlitData.windowFBO); CreateOutputWindowBackbuffer(outw); return true; } return false; }
vector<byte> GLReplay::GetBufferData(ResourceId buff, uint32_t offset, uint32_t len) { vector<byte> ret; if(m_pDriver->m_Buffers.find(buff) == m_pDriver->m_Buffers.end()) { RDCWARN("Requesting data for non-existant buffer %llu", buff); return ret; } auto &buf = m_pDriver->m_Buffers[buff]; if(len > 0 && offset+len > buf.size) { RDCWARN("Attempting to read off the end of the array. Will be clamped"); len = RDCMIN(len, uint32_t(buf.size-offset)); } else if(len == 0) { len = (uint32_t)buf.size; } ret.resize(len); WrappedOpenGL &gl = *m_pDriver; MakeCurrentReplayContext(m_DebugCtx); gl.glBindBuffer(eGL_COPY_READ_BUFFER, buf.resource.name); gl.glGetBufferSubData(eGL_COPY_READ_BUFFER, (GLintptr)offset, (GLsizeiptr)len, &ret[0]); return ret; }
void GLReplay::InitOutputWindow(OutputWindow &outwin) { if(m_pDriver == NULL) return; MakeCurrentReplayContext(&outwin); WrappedOpenGL &gl = *m_pDriver; gl.glGenVertexArrays(1, &outwin.BlitData.emptyVAO); gl.glBindVertexArray(outwin.BlitData.emptyVAO); }
void GLReplay::ClearOutputWindowDepth(uint64_t id, float depth, uint8_t stencil) { if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end()) return; OutputWindow &outw = m_OutputWindows[id]; MakeCurrentReplayContext(&outw); m_pDriver->glClearBufferfv(eGL_DEPTH, 0, &depth); }
void GLReplay::ClearOutputWindowColour(uint64_t id, float col[4]) { if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end()) return; OutputWindow &outw = m_OutputWindows[id]; MakeCurrentReplayContext(m_DebugCtx); m_pDriver->glClearBufferfv(eGL_COLOR, 0, col); }
void GLReplay::BindOutputWindow(uint64_t id, bool depth) { if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end()) return; OutputWindow &outw = m_OutputWindows[id]; MakeCurrentReplayContext(m_DebugCtx); m_pDriver->glBindFramebuffer(eGL_FRAMEBUFFER, outw.BlitData.windowFBO); m_pDriver->glViewport(0, 0, outw.width, outw.height); DebugData.outWidth = float(outw.width); DebugData.outHeight = float(outw.height); }
GLuint GLReplay::CreateShaderProgram(const char *vsSrc, const char *psSrc) { if(m_pDriver == NULL) return 0; MakeCurrentReplayContext(m_DebugCtx); WrappedOpenGL &gl = *m_pDriver; GLuint vs = gl.glCreateShader(eGL_VERTEX_SHADER); GLuint fs = gl.glCreateShader(eGL_FRAGMENT_SHADER); const char *src = vsSrc; gl.glShaderSource(vs, 1, &src, NULL); src = psSrc; gl.glShaderSource(fs, 1, &src, NULL); gl.glCompileShader(vs); gl.glCompileShader(fs); char buffer[4096]; GLint status = 0; gl.glGetShaderiv(vs, eGL_COMPILE_STATUS, &status); if(status == 0) { gl.glGetShaderInfoLog(vs, 4096, NULL, buffer); RDCERR("Shader error: %hs", buffer); } gl.glGetShaderiv(fs, eGL_COMPILE_STATUS, &status); if(status == 0) { gl.glGetShaderInfoLog(fs, 4096, NULL, buffer); RDCERR("Shader error: %hs", buffer); } GLuint ret = gl.glCreateProgram(); gl.glAttachShader(ret, vs); gl.glAttachShader(ret, fs); gl.glLinkProgram(ret); gl.glDeleteShader(vs); gl.glDeleteShader(fs); return ret; }
void GLReplay::DestroyOutputWindow(uint64_t id) { auto it = m_OutputWindows.find(id); if(id == 0 || it == m_OutputWindows.end()) return; OutputWindow &outw = it->second; MakeCurrentReplayContext(&outw); WrappedOpenGL &gl = *m_pDriver; gl.glDeleteFramebuffers(1, &outw.BlitData.readFBO); glXMakeContextCurrentProc(outw.dpy, 0L, 0L, NULL); glXDestroyCtxProc(outw.dpy, outw.ctx); m_OutputWindows.erase(it); }
void GLReplay::RenderCheckerboard(Vec3f light, Vec3f dark) { MakeCurrentReplayContext(m_DebugCtx); WrappedOpenGL &gl = *m_pDriver; gl.glUseProgram(DebugData.checkerProg); gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, DebugData.UBOs[0]); Vec4f *ubo = (Vec4f *)gl.glMapBufferRange(eGL_UNIFORM_BUFFER, 0, sizeof(Vec4f)*2, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); ubo[0] = Vec4f(light.x, light.y, light.z, 1.0f); ubo[1] = Vec4f(dark.x, dark.y, dark.z, 1.0f); gl.glUnmapBuffer(eGL_UNIFORM_BUFFER); gl.glBindVertexArray(DebugData.emptyVAO); gl.glDrawArrays(eGL_TRIANGLE_STRIP, 0, 4); }
void GLReplay::RenderHighlightBox(float w, float h, float scale) { MakeCurrentReplayContext(m_DebugCtx); const float xpixdim = 2.0f/w; const float ypixdim = 2.0f/h; const float xdim = scale*xpixdim; const float ydim = scale*ypixdim; WrappedOpenGL &gl = *m_pDriver; gl.glUseProgram(DebugData.genericProg); GLint offsetLoc = gl.glGetUniformLocation(DebugData.genericProg, "RENDERDOC_GenericVS_Offset"); GLint scaleLoc = gl.glGetUniformLocation(DebugData.genericProg, "RENDERDOC_GenericVS_Scale"); GLint colLoc = gl.glGetUniformLocation(DebugData.genericProg, "RENDERDOC_GenericFS_Color"); Vec4f offsetVal(0.0f, 0.0f, 0.0f, 0.0f); Vec4f scaleVal(xdim, ydim, 1.0f, 1.0f); Vec4f colVal(1.0f, 1.0f, 1.0f, 1.0f); gl.glUniform4fv(offsetLoc, 1, &offsetVal.x); gl.glUniform4fv(scaleLoc, 1, &scaleVal.x); gl.glUniform4fv(colLoc, 1, &colVal.x); gl.glBindVertexArray(DebugData.outlineStripVAO); gl.glDrawArrays(eGL_LINE_LOOP, 0, 4); offsetVal = Vec4f(-xpixdim, ypixdim, 0.0f, 0.0f); scaleVal = Vec4f(xdim+xpixdim*2, ydim+ypixdim*2, 1.0f, 1.0f); colVal = Vec4f(0.0f, 0.0f, 0.0f, 1.0f); gl.glUniform4fv(offsetLoc, 1, &offsetVal.x); gl.glUniform4fv(scaleLoc, 1, &scaleVal.x); gl.glUniform4fv(colLoc, 1, &colVal.x); gl.glBindVertexArray(DebugData.outlineStripVAO); gl.glDrawArrays(eGL_LINE_LOOP, 0, 4); }
void GLReplay::PickPixel(ResourceId texture, uint32_t x, uint32_t y, uint32_t sliceFace, uint32_t mip, float pixel[4]) { WrappedOpenGL &gl = *m_pDriver; MakeCurrentReplayContext(m_DebugCtx); gl.glBindFramebuffer(eGL_FRAMEBUFFER, DebugData.pickPixelFBO); gl.glBindFramebuffer(eGL_READ_FRAMEBUFFER, DebugData.pickPixelFBO); pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0.0f; gl.glClearBufferfv(eGL_COLOR, 0, pixel); DebugData.outWidth = DebugData.outHeight = 1.0f; gl.glViewport(0, 0, 1, 1); { TextureDisplay texDisplay; texDisplay.Red = texDisplay.Green = texDisplay.Blue = texDisplay.Alpha = true; texDisplay.HDRMul = -1.0f; texDisplay.mip = mip; texDisplay.CustomShader = ResourceId(); texDisplay.sliceFace = sliceFace; texDisplay.rangemin = 0.0f; texDisplay.rangemax = 1.0f; texDisplay.scale = 1.0f; texDisplay.texid = texture; texDisplay.rawoutput = true; texDisplay.offx = -float(x); texDisplay.offy = -float(y); RenderTexture(texDisplay); } gl.glReadPixels(0, 0, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)pixel); }
void GLReplay::FlipOutputWindow(uint64_t id) { if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end()) return; OutputWindow &outw = m_OutputWindows[id]; MakeCurrentReplayContext(&outw); WrappedOpenGL &gl = *m_pDriver; gl.glBindFramebuffer(eGL_FRAMEBUFFER, 0); gl.glViewport(0, 0, outw.width, outw.height); gl.glUseProgram(DebugData.blitProg); gl.glActiveTexture(eGL_TEXTURE0); gl.glBindTexture(eGL_TEXTURE_2D, outw.BlitData.backbuffer); gl.glBindVertexArray(outw.BlitData.emptyVAO); gl.glDrawArrays(eGL_TRIANGLE_STRIP, 0, 4); SwapBuffers(&outw); }
vector<CounterResult> GLReplay::FetchCounters(const vector<uint32_t> &counters) { vector<CounterResult> ret; if(counters.empty()) { RDCERR("No counters specified to FetchCounters"); return ret; } MakeCurrentReplayContext(&m_ReplayCtx); GLCounterContext ctx; for(int loop = 0; loop < 1; loop++) { ctx.eventStart = 0; ctx.reuseIdx = loop == 0 ? -1 : 0; m_pDriver->SetFetchCounters(true); FillTimers(ctx, m_pDriver->GetRootDraw(), counters); m_pDriver->SetFetchCounters(false); double nanosToSecs = 1.0 / 1000000000.0; GLuint prevbind = 0; m_pDriver->glGetIntegerv(eGL_QUERY_BUFFER_BINDING, (GLint *)&prevbind); m_pDriver->glBindBuffer(eGL_QUERY_BUFFER, 0); for(size_t i = 0; i < ctx.queries.size(); i++) { for(uint32_t c = 0; c < counters.size(); c++) { if(ctx.queries[i].obj[counters[c]]) { GLuint64 data = 0; m_pDriver->glGetQueryObjectui64v(ctx.queries[i].obj[counters[c]], eGL_QUERY_RESULT, &data); double duration = double(data) * nanosToSecs; if(m_pDriver->glGetError()) { data = (uint64_t)-1; duration = -1; } if(counters[c] == eCounter_EventGPUDuration) { ret.push_back(CounterResult(ctx.queries[i].eventID, eCounter_EventGPUDuration, duration)); } else ret.push_back(CounterResult(ctx.queries[i].eventID, counters[c], data)); } else ret.push_back(CounterResult(ctx.queries[i].eventID, counters[c], (uint64_t)-1)); } } m_pDriver->glBindBuffer(eGL_QUERY_BUFFER, prevbind); } for(size_t i = 0; i < ctx.queries.size(); i++) for(uint32_t c = 0; c < counters.size(); c++) if(ctx.queries[i].obj[counters[c]]) m_pDriver->glDeleteQueries(1, &ctx.queries[i].obj[counters[c]]); return ret; }
bool GLReplay::RenderTexture(TextureDisplay cfg) { MakeCurrentReplayContext(m_DebugCtx); WrappedOpenGL &gl = *m_pDriver; gl.glUseProgram(DebugData.texDisplayProg); auto &texDetails = m_pDriver->m_Textures[cfg.texid]; gl.glActiveTexture(eGL_TEXTURE0); gl.glBindTexture(eGL_TEXTURE_2D, texDetails.resource.name); if(cfg.mip == 0 && cfg.scale < 1.0f) gl.glBindSampler(0, DebugData.linearSampler); else gl.glBindSampler(0, DebugData.pointSampler); GLint tex_x = texDetails.width, tex_y = texDetails.height, tex_z = texDetails.depth; gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, DebugData.UBOs[0]); struct uboData { Vec2f Position; float Scale; float HDRMul; Vec4f Channels; float RangeMinimum; float InverseRangeSize; float MipLevel; float dummy2; Vec3f TextureResolutionPS; int OutputDisplayFormat; Vec2f OutputRes; int RawOutput; float Slice; }; uboData *ubo = (uboData *)gl.glMapBufferRange(eGL_UNIFORM_BUFFER, 0, sizeof(uboData), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); RDCCOMPILE_ASSERT(sizeof(uboData) <= DebugData.UBOSize, "UBO data is too big"); float x = cfg.offx; float y = cfg.offy; ubo->Position.x = x; ubo->Position.y = y; ubo->Scale = cfg.scale; if(cfg.scale <= 0.0f) { float xscale = DebugData.outWidth/float(tex_x); float yscale = DebugData.outHeight/float(tex_y); ubo->Scale = RDCMIN(xscale, yscale); if(yscale > xscale) { ubo->Position.x = 0; ubo->Position.y = (DebugData.outHeight-(tex_y*ubo->Scale) )*0.5f; } else { ubo->Position.y = 0; ubo->Position.x = (DebugData.outWidth-(tex_x*ubo->Scale) )*0.5f; } } ubo->HDRMul = cfg.HDRMul; if(cfg.rangemax <= cfg.rangemin) cfg.rangemax += 0.00001f; ubo->Channels.x = cfg.Red ? 1.0f : 0.0f; ubo->Channels.y = cfg.Green ? 1.0f : 0.0f; ubo->Channels.z = cfg.Blue ? 1.0f : 0.0f; ubo->Channels.w = cfg.Alpha ? 1.0f : 0.0f; ubo->RangeMinimum = cfg.rangemin; ubo->InverseRangeSize = 1.0f/(cfg.rangemax-cfg.rangemin); ubo->MipLevel = (float)cfg.mip; ubo->OutputDisplayFormat = 0x2; // 2d. Unused for now ubo->RawOutput = cfg.rawoutput ? 1 : 0; ubo->TextureResolutionPS.x = float(tex_x); ubo->TextureResolutionPS.y = float(tex_y); ubo->TextureResolutionPS.z = float(tex_z); ubo->OutputRes.x = DebugData.outWidth; ubo->OutputRes.y = DebugData.outHeight; gl.glUnmapBuffer(eGL_UNIFORM_BUFFER); if(cfg.rawoutput) { gl.glDisable(eGL_BLEND); } else { gl.glEnable(eGL_BLEND); gl.glBlendFunc(eGL_SRC_ALPHA, eGL_ONE_MINUS_SRC_ALPHA); } gl.glBindVertexArray(DebugData.emptyVAO); gl.glDrawArrays(eGL_TRIANGLE_STRIP, 0, 4); gl.glBindSampler(0, 0); return true; }
void GLReplay::SavePipelineState() { GLPipelineState &pipe = m_CurPipelineState; WrappedOpenGL &gl = *m_pDriver; GLResourceManager *rm = m_pDriver->GetResourceManager(); MakeCurrentReplayContext(&m_ReplayCtx); // Index buffer pipe.m_VtxIn.ibuffer.Offset = m_pDriver->m_LastIndexOffset; pipe.m_VtxIn.ibuffer.Format = ResourceFormat(); pipe.m_VtxIn.ibuffer.Format.special = false; pipe.m_VtxIn.ibuffer.Format.compCount = 1; pipe.m_VtxIn.ibuffer.Format.compType = eCompType_UInt; switch(m_pDriver->m_LastIndexSize) { default: break; case eGL_UNSIGNED_BYTE: pipe.m_VtxIn.ibuffer.Format.compByteWidth = 1; pipe.m_VtxIn.ibuffer.Format.strname = L"GL_UNSIGNED_BYTE"; break; case eGL_UNSIGNED_SHORT: pipe.m_VtxIn.ibuffer.Format.compByteWidth = 2; pipe.m_VtxIn.ibuffer.Format.strname = L"GL_UNSIGNED_SHORT"; break; case eGL_UNSIGNED_INT: pipe.m_VtxIn.ibuffer.Format.compByteWidth = 4; pipe.m_VtxIn.ibuffer.Format.strname = L"GL_UNSIGNED_INT"; break; } GLint curIdxBuf = 0; gl.glGetIntegerv(eGL_ELEMENT_ARRAY_BUFFER_BINDING, &curIdxBuf); pipe.m_VtxIn.ibuffer.Buffer = rm->GetOriginalID(rm->GetID(BufferRes(curIdxBuf))); // Vertex buffers and attributes GLint numVBufferBindings = 16; gl.glGetIntegerv(eGL_MAX_VERTEX_ATTRIB_BINDINGS, &numVBufferBindings); GLint numVAttribBindings = 16; gl.glGetIntegerv(eGL_MAX_VERTEX_ATTRIBS, &numVAttribBindings); create_array_uninit(pipe.m_VtxIn.vbuffers, numVBufferBindings); create_array_uninit(pipe.m_VtxIn.attributes, numVAttribBindings); for(GLuint i=0; i < (GLuint)numVBufferBindings; i++) { GLint vb = 0; gl.glGetIntegeri_v(eGL_VERTEX_BINDING_BUFFER, i, &vb); pipe.m_VtxIn.vbuffers[i].Buffer = rm->GetOriginalID(rm->GetID(BufferRes(vb))); gl.glGetIntegeri_v(eGL_VERTEX_BINDING_STRIDE, i, (GLint *)&pipe.m_VtxIn.vbuffers[i].Stride); gl.glGetIntegeri_v(eGL_VERTEX_BINDING_OFFSET, i, (GLint *)&pipe.m_VtxIn.vbuffers[i].Offset); gl.glGetIntegeri_v(eGL_VERTEX_BINDING_DIVISOR, i, (GLint *)&pipe.m_VtxIn.vbuffers[i].Divisor); pipe.m_VtxIn.vbuffers[i].PerInstance = (pipe.m_VtxIn.vbuffers[i].Divisor != 0); } for(GLuint i=0; i < (GLuint)numVAttribBindings; i++) { gl.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_ENABLED, (GLint *)&pipe.m_VtxIn.attributes[i].Enabled); gl.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_BINDING, (GLint *)&pipe.m_VtxIn.attributes[i].BufferSlot); gl.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_RELATIVE_OFFSET, (GLint*)&pipe.m_VtxIn.attributes[i].RelativeOffset); GLenum type = eGL_FLOAT; GLint normalized = 0; gl.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_TYPE, (GLint *)&type); gl.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &normalized); ResourceFormat fmt; fmt.special = false; fmt.compCount = 4; gl.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_SIZE, (GLint *)&fmt.compCount); switch(type) { default: case eGL_BYTE: fmt.compByteWidth = 1; fmt.compType = normalized ? eCompType_SInt : eCompType_SNorm; fmt.strname = StringFormat::WFmt(L"GL_BYTE%d", fmt.compCount) + (normalized ? L"" : L"_SNORM"); break; case eGL_UNSIGNED_BYTE: fmt.compByteWidth = 1; fmt.compType = normalized ? eCompType_UInt : eCompType_UNorm; fmt.strname = StringFormat::WFmt(L"GL_UNSIGNED_BYTE%d", fmt.compCount) + (normalized ? L"" : L"_UNORM"); break; case eGL_SHORT: fmt.compByteWidth = 2; fmt.compType = normalized ? eCompType_SInt : eCompType_SNorm; fmt.strname = StringFormat::WFmt(L"GL_SHORT%d", fmt.compCount) + (normalized ? L"" : L"_SNORM"); break; case eGL_UNSIGNED_SHORT: fmt.compByteWidth = 2; fmt.compType = normalized ? eCompType_UInt : eCompType_UNorm; fmt.strname = StringFormat::WFmt(L"GL_UNSIGNED_SHORT%d", fmt.compCount) + (normalized ? L"" : L"_UNORM"); break; case eGL_INT: fmt.compByteWidth = 4; fmt.compType = normalized ? eCompType_SInt : eCompType_SNorm; fmt.strname = StringFormat::WFmt(L"GL_INT%d", fmt.compCount) + (normalized ? L"" : L"_SNORM"); break; case eGL_UNSIGNED_INT: fmt.compByteWidth = 4; fmt.compType = normalized ? eCompType_UInt : eCompType_UNorm; fmt.strname = StringFormat::WFmt(L"GL_UNSIGNED_INT%d", fmt.compCount) + (normalized ? L"" : L"_UNORM"); break; case eGL_FLOAT: fmt.compByteWidth = 4; fmt.compType = eCompType_Float; fmt.strname = StringFormat::WFmt(L"GL_FLOAT%d", fmt.compCount); break; case eGL_DOUBLE: fmt.compByteWidth = 8; fmt.compType = eCompType_Double; fmt.strname = StringFormat::WFmt(L"GL_DOUBLE%d", fmt.compCount); break; case eGL_HALF_FLOAT: fmt.compByteWidth = 2; fmt.compType = eCompType_Float; fmt.strname = StringFormat::WFmt(L"GL_HALF_FLOAT%d", fmt.compCount); break; case eGL_INT_2_10_10_10_REV: fmt.special = true; fmt.specialFormat = eSpecial_R10G10B10A2; fmt.compCount = 4; fmt.compType = eCompType_UInt; fmt.strname = L"GL_INT_2_10_10_10_REV"; break; case eGL_UNSIGNED_INT_2_10_10_10_REV: fmt.special = true; fmt.specialFormat = eSpecial_R10G10B10A2; fmt.compCount = 4; fmt.compType = eCompType_SInt; fmt.strname = L"eGL_UNSIGNED_INT_2_10_10_10_REV"; break; case eGL_UNSIGNED_INT_10F_11F_11F_REV: fmt.special = true; fmt.specialFormat = eSpecial_R11G11B10; fmt.compCount = 3; fmt.compType = eCompType_SInt; fmt.strname = L"eGL_UNSIGNED_INT_10F_11F_11F_REV"; break; } pipe.m_VtxIn.attributes[i].Format = fmt; } switch(m_pDriver->m_LastDrawMode) { default: pipe.m_VtxIn.Topology = eTopology_Unknown; break; case GL_POINTS: pipe.m_VtxIn.Topology = eTopology_PointList; break; case GL_LINE_STRIP: pipe.m_VtxIn.Topology = eTopology_LineStrip; break; case GL_LINE_LOOP: pipe.m_VtxIn.Topology = eTopology_LineLoop; break; case GL_LINES: pipe.m_VtxIn.Topology = eTopology_LineList; break; case GL_LINE_STRIP_ADJACENCY: pipe.m_VtxIn.Topology = eTopology_LineStrip_Adj; break; case GL_LINES_ADJACENCY: pipe.m_VtxIn.Topology = eTopology_LineList_Adj; break; case GL_TRIANGLE_STRIP: pipe.m_VtxIn.Topology = eTopology_TriangleStrip; break; case GL_TRIANGLE_FAN: pipe.m_VtxIn.Topology = eTopology_TriangleFan; break; case GL_TRIANGLES: pipe.m_VtxIn.Topology = eTopology_TriangleList; break; case GL_TRIANGLE_STRIP_ADJACENCY: pipe.m_VtxIn.Topology = eTopology_TriangleStrip_Adj; break; case GL_TRIANGLES_ADJACENCY: pipe.m_VtxIn.Topology = eTopology_TriangleList_Adj; break; case GL_PATCHES: { GLint patchCount = 3; gl.glGetIntegerv(eGL_PATCH_VERTICES, &patchCount); pipe.m_VtxIn.Topology = PrimitiveTopology(eTopology_PatchList_1CPs+patchCount); break; } } // Shader stages GLuint curProg = 0; gl.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint*)&curProg); auto &progDetails = m_pDriver->m_Programs[rm->GetID(ProgramRes(curProg))]; RDCASSERT(progDetails.shaders.size()); for(size_t i=0; i < progDetails.shaders.size(); i++) { if(m_pDriver->m_Shaders[ progDetails.shaders[i] ].type == eGL_VERTEX_SHADER) pipe.m_VS.Shader = rm->GetOriginalID(progDetails.shaders[i]); else if(m_pDriver->m_Shaders[ progDetails.shaders[i] ].type == eGL_FRAGMENT_SHADER) pipe.m_FS.Shader = rm->GetOriginalID(progDetails.shaders[i]); } pipe.m_VS.stage = eShaderStage_Vertex; pipe.m_TCS.stage = eShaderStage_Tess_Control; pipe.m_TES.stage = eShaderStage_Tess_Eval; pipe.m_GS.stage = eShaderStage_Geometry; pipe.m_FS.stage = eShaderStage_Fragment; pipe.m_CS.stage = eShaderStage_Compute; // Textures GLint numTexUnits = 8; gl.glGetIntegerv(eGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numTexUnits); create_array_uninit(pipe.Textures, numTexUnits); GLenum activeTexture = eGL_TEXTURE0; gl.glGetIntegerv(eGL_ACTIVE_TEXTURE, (GLint*)&activeTexture); // GL is ass-backwards in its handling of texture units. When a shader is active // the types in the glsl samplers inform which targets are used from which texture units // // So texture unit 5 can have a 2D bound (texture 52) and a Cube bound (texture 77). // * if a uniform sampler2D has value 5 then the 2D texture is used, and we sample from 52 // * if a uniform samplerCube has value 5 then the Cube texture is used, and we sample from 77 // It's illegal for both a sampler2D and samplerCube to both have the same value (or any two // different types). It makes it all rather pointless and needlessly complex. // // What we have to do then, is consider the program, look at the values of the uniforms, and // then get the appropriate current binding based on the uniform type. We can warn/alert the // user if we hit the illegal case of two uniforms with different types but the same value // // Handling is different if no shaders are active, but we don't consider that case. // prefetch uniform values in GetShader() ShaderReflection *refls[6]; for(size_t s=0; s < progDetails.shaders.size(); s++) refls[s] = GetShader(progDetails.shaders[s]); for(GLint unit=0; unit < numTexUnits; unit++) { GLenum binding = eGL_UNKNOWN_ENUM; GLenum target = eGL_UNKNOWN_ENUM; for(size_t s=0; s < progDetails.shaders.size(); s++) { if(refls[s] == NULL) continue; for(int32_t r=0; r < refls[s]->Resources.count; r++) { // bindPoint is the uniform value for this sampler if(refls[s]->Resources[r].bindPoint == (uint32_t)unit) { GLenum t = eGL_UNKNOWN_ENUM; switch(refls[s]->Resources[r].resType) { case eResType_None: t = eGL_UNKNOWN_ENUM; break; case eResType_Buffer: t = eGL_TEXTURE_BINDING_BUFFER; break; case eResType_Texture1D: t = eGL_TEXTURE_BINDING_1D; target = eGL_TEXTURE_1D; break; case eResType_Texture1DArray: t = eGL_TEXTURE_BINDING_1D_ARRAY; target = eGL_TEXTURE_1D_ARRAY; break; case eResType_Texture2D: t = eGL_TEXTURE_BINDING_2D; target = eGL_TEXTURE_2D; break; case eResType_Texture2DArray: t = eGL_TEXTURE_BINDING_2D_ARRAY; target = eGL_TEXTURE_2D_ARRAY; break; case eResType_Texture2DMS: t = eGL_TEXTURE_BINDING_2D_MULTISAMPLE; target = eGL_TEXTURE_2D_MULTISAMPLE; break; case eResType_Texture2DMSArray: t = eGL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY; target = eGL_TEXTURE_2D_MULTISAMPLE_ARRAY; break; case eResType_Texture3D: t = eGL_TEXTURE_BINDING_3D; target = eGL_TEXTURE_3D; break; case eResType_TextureCube: t = eGL_TEXTURE_BINDING_CUBE_MAP; target = eGL_TEXTURE_CUBE_MAP; break; case eResType_TextureCubeArray: t = eGL_TEXTURE_BINDING_CUBE_MAP_ARRAY; target = eGL_TEXTURE_CUBE_MAP_ARRAY; break; } if(binding == eGL_UNKNOWN_ENUM) { binding = t; } else if(binding == t) { // two uniforms with the same type pointing to the same slot is fine binding = t; } else if(binding != t) { RDCWARN("Two uniforms pointing to texture unit %d with types %s and %s", unit, ToStr::Get(binding).c_str(), ToStr::Get(t).c_str()); } } } } if(binding != eGL_UNKNOWN_ENUM) { gl.glActiveTexture(GLenum(eGL_TEXTURE0+unit)); GLuint tex; gl.glGetIntegerv(binding, (GLint *)&tex); // very bespoke/specific GLint firstSlice = 0; gl.glGetTexParameteriv(target, eGL_TEXTURE_VIEW_MIN_LEVEL, &firstSlice); pipe.Textures[unit].Resource = rm->GetOriginalID(rm->GetID(TextureRes(tex))); pipe.Textures[unit].FirstSlice = (uint32_t)firstSlice; } else { // what should we do in this case? there could be something bound just not used, // it'd be nice to return that } } gl.glActiveTexture(activeTexture); GLuint curFBO = 0; gl.glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&curFBO); GLint numCols = 8; gl.glGetIntegerv(eGL_MAX_COLOR_ATTACHMENTS, &numCols); GLuint curCol[32] = { 0 }; GLuint curDepth = 0; GLuint curStencil = 0; RDCASSERT(numCols <= 32); // we should never bind the true default framebuffer - if the app did, we will have our fake bound RDCASSERT(curFBO != 0); { for(GLint i=0; i < numCols; i++) gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0+i), eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&curCol[i]); gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&curDepth); gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&curStencil); } pipe.m_FB.FBO = rm->GetOriginalID(rm->GetID(FramebufferRes(curFBO))); create_array_uninit(pipe.m_FB.Color, numCols); for(GLint i=0; i < numCols; i++) pipe.m_FB.Color[i] = rm->GetOriginalID(rm->GetID(TextureRes(curCol[i]))); pipe.m_FB.Depth = rm->GetOriginalID(rm->GetID(TextureRes(curDepth))); pipe.m_FB.Stencil = rm->GetOriginalID(rm->GetID(TextureRes(curStencil))); }
void GLReplay::ReplayLog(uint32_t frameID, uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType) { MakeCurrentReplayContext(&m_ReplayCtx); m_pDriver->ReplayLog(frameID, startEventID, endEventID, replayType); }
ShaderReflection *GLReplay::GetShader(ResourceId id) { WrappedOpenGL &gl = *m_pDriver; MakeCurrentReplayContext(&m_ReplayCtx); GLuint curProg = 0; gl.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint*)&curProg); auto &progDetails = m_pDriver->m_Programs[m_pDriver->GetResourceManager()->GetID(ProgramRes(curProg))]; auto &shaderDetails = m_pDriver->m_Shaders[id]; auto &refl = shaderDetails.reflection; // initialise reflection data // TODO: do this earlier. In glLinkProgram? if(refl.DebugInfo.files.count == 0) { refl.DebugInfo.entryFunc = "main"; refl.DebugInfo.compileFlags = 0; create_array_uninit(refl.DebugInfo.files, shaderDetails.sources.size()); for(size_t i=0; i < shaderDetails.sources.size(); i++) { refl.DebugInfo.files[i].first = StringFormat::Fmt("source%u.glsl", (uint32_t)i); refl.DebugInfo.files[i].second = shaderDetails.sources[i]; } refl.Disassembly = ""; vector<ShaderResource> resources; GLint numUniforms = 0; gl.glGetProgramInterfaceiv(curProg, eGL_UNIFORM, eGL_ACTIVE_RESOURCES, &numUniforms); const size_t numProps = 6; GLenum resProps[numProps] = { eGL_REFERENCED_BY_VERTEX_SHADER, eGL_TYPE, eGL_NAME_LENGTH, eGL_LOCATION, eGL_BLOCK_INDEX, eGL_ARRAY_SIZE, }; if(shaderDetails.type == eGL_VERTEX_SHADER) resProps[0] = eGL_REFERENCED_BY_VERTEX_SHADER; if(shaderDetails.type == eGL_TESS_CONTROL_SHADER) resProps[0] = eGL_REFERENCED_BY_TESS_CONTROL_SHADER; if(shaderDetails.type == eGL_TESS_EVALUATION_SHADER) resProps[0] = eGL_REFERENCED_BY_TESS_EVALUATION_SHADER; if(shaderDetails.type == eGL_GEOMETRY_SHADER) resProps[0] = eGL_REFERENCED_BY_GEOMETRY_SHADER; if(shaderDetails.type == eGL_FRAGMENT_SHADER) resProps[0] = eGL_REFERENCED_BY_FRAGMENT_SHADER; if(shaderDetails.type == eGL_COMPUTE_SHADER) resProps[0] = eGL_REFERENCED_BY_COMPUTE_SHADER; for(GLint u=0; u < numUniforms; u++) { GLint values[numProps]; gl.glGetProgramResourceiv(curProg, eGL_UNIFORM, u, numProps, resProps, numProps, NULL, values); // skip if unused by this stage if(values[0] == GL_FALSE) continue; ShaderResource res; res.IsSampler = false; // no separate sampler objects in GL if(values[1] == GL_SAMPLER_2D) { res.IsSRV = true; res.IsTexture = true; res.IsUAV = false; res.resType = eResType_Texture2D; res.variableType.descriptor.name = "sampler2D"; res.variableType.descriptor.rows = 1; res.variableType.descriptor.cols = 4; res.variableType.descriptor.elements = 1; } else if(values[1] == GL_INT_SAMPLER_1D) { res.IsSRV = true; res.IsTexture = true; res.IsUAV = false; res.resType = eResType_Texture1D; res.variableType.descriptor.name = "isampler1D"; res.variableType.descriptor.rows = 1; res.variableType.descriptor.cols = 4; res.variableType.descriptor.elements = 1; } else { // fill in more sampler types continue; } res.variableAddress = values[3]; create_array_uninit(res.name, values[2]+1); gl.glGetProgramResourceName(curProg, eGL_UNIFORM, u, values[2]+1, NULL, res.name.elems); res.name.count--; // trim off trailing null resources.push_back(res); } refl.Resources = resources; vector<ShaderConstant> globalUniforms; for(GLint u=0; u < numUniforms; u++) { GLint values[numProps]; gl.glGetProgramResourceiv(curProg, eGL_UNIFORM, u, numProps, resProps, numProps, NULL, values); // skip if unused by this stage if(values[0] == GL_FALSE) continue; // don't look at block uniforms just yet if(values[4] != -1) { GLNOTIMP("Not fetching uniforms in UBOs (should become their own ConstantBlocks)"); continue; } ShaderConstant var; if(values[1] == GL_FLOAT_VEC4) { var.type.descriptor.name = "vec4"; var.type.descriptor.rows = 1; var.type.descriptor.cols = 4; var.type.descriptor.elements = values[5]; } else if(values[1] == GL_FLOAT_VEC3) { var.type.descriptor.name = "vec3"; var.type.descriptor.rows = 1; var.type.descriptor.cols = 3; var.type.descriptor.elements = values[5]; } else if(values[1] == GL_FLOAT_MAT4) { var.type.descriptor.name = "mat4"; var.type.descriptor.rows = 4; var.type.descriptor.cols = 4; var.type.descriptor.elements = values[5]; } else { // fill in more uniform types continue; } var.reg.vec = values[3]; var.reg.comp = 0; create_array_uninit(var.name, values[2]+1); gl.glGetProgramResourceName(curProg, eGL_UNIFORM, u, values[2]+1, NULL, var.name.elems); var.name.count--; // trim off trailing null if(strchr(var.name.elems, '.')) { GLNOTIMP("Variable contains . - structure not reconstructed"); } globalUniforms.push_back(var); } vector<ConstantBlock> cbuffers; if(!globalUniforms.empty()) { ConstantBlock globals; globals.name = "Globals"; globals.bufferAddress = -1; globals.variables = globalUniforms; cbuffers.push_back(globals); } // here we would iterate over UNIFORM_BLOCKs or similar // TODO: fill in Interfaces with shader subroutines? // TODO: find a way of generating input/output signature. // The only way I can think of doing this is to generate separable programs for each // shader stage, but that requires modifying the glsl to redeclare built-in blocks if necessary. refl.ConstantBlocks = cbuffers; } // update samplers with latest uniform values for(int32_t i=0; i < refl.Resources.count; i++) { if(refl.Resources.elems[i].IsSRV && refl.Resources.elems[i].IsTexture) gl.glGetUniformiv(curProg, refl.Resources.elems[i].variableAddress, (GLint *)&refl.Resources.elems[i].bindPoint); } return &refl; }
void GLReplay::ReadLogInitialisation() { MakeCurrentReplayContext(&m_ReplayCtx); m_pDriver->ReadLogInitialisation(); }
uint64_t GLReplay::MakeOutputWindow(WindowingSystem system, void *data, bool depth) { Display *dpy = NULL; Drawable draw = 0; if(system == eWindowingSystem_Xlib) { #if ENABLED(RDOC_XLIB) XlibWindowData *xlib = (XlibWindowData *)data; dpy = xlib->display; draw = xlib->window; #else RDCERR( "Xlib windowing system data passed in, but support is not compiled in. GL must have xlib " "support compiled in"); #endif } else if(system == eWindowingSystem_Unknown) { // allow undefined so that internally we can create a window-less context dpy = XOpenDisplay(NULL); if(dpy == NULL) return 0; } else { RDCERR("Unexpected window system %u", system); } static int visAttribs[] = {GLX_X_RENDERABLE, True, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_DOUBLEBUFFER, True, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, True, 0}; int numCfgs = 0; GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs); if(fbcfg == NULL) { XCloseDisplay(dpy); RDCERR("Couldn't choose default framebuffer config"); return eReplayCreate_APIInitFailed; } if(draw != 0) { // Choose FB config with a GLX_VISUAL_ID that matches the X screen. VisualID visualid_correct = DefaultVisual(dpy, DefaultScreen(dpy))->visualid; for(int i = 0; i < numCfgs; i++) { int visualid; glXGetFBConfigAttrib(dpy, fbcfg[i], GLX_VISUAL_ID, &visualid); if((VisualID)visualid == visualid_correct) { fbcfg[0] = fbcfg[i]; break; } } } int attribs[64] = {0}; int i = 0; attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB; attribs[i++] = 4; attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB; attribs[i++] = 3; attribs[i++] = GLX_CONTEXT_FLAGS_ARB; #if ENABLED(RDOC_DEVEL) attribs[i++] = GLX_CONTEXT_DEBUG_BIT_ARB; #else attribs[i++] = 0; #endif attribs[i++] = GLX_CONTEXT_PROFILE_MASK_ARB; attribs[i++] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; GLXContext ctx = glXCreateContextAttribsProc(dpy, fbcfg[0], m_ReplayCtx.ctx, true, attribs); if(ctx == NULL) { XCloseDisplay(dpy); RDCERR("Couldn't create 4.3 context - RenderDoc requires OpenGL 4.3 availability"); return 0; } GLXDrawable wnd = 0; if(draw == 0) { // don't care about pbuffer properties as we won't render directly to this int pbAttribs[] = {GLX_PBUFFER_WIDTH, 32, GLX_PBUFFER_HEIGHT, 32, 0}; wnd = glXCreatePbufferProc(dpy, fbcfg[0], pbAttribs); } else { wnd = glXCreateWindow(dpy, fbcfg[0], draw, 0); } XFree(fbcfg); OutputWindow win; win.dpy = dpy; win.ctx = ctx; win.wnd = wnd; glXQueryDrawableProc(dpy, wnd, GLX_WIDTH, (unsigned int *)&win.width); glXQueryDrawableProc(dpy, wnd, GLX_HEIGHT, (unsigned int *)&win.height); MakeCurrentReplayContext(&win); InitOutputWindow(win); CreateOutputWindowBackbuffer(win, depth); uint64_t ret = m_OutputWindowID++; m_OutputWindows[ret] = win; return ret; }
void GLReplay::InitDebugData() { if(m_pDriver == NULL) return; { uint64_t id = MakeOutputWindow(NULL, true); m_DebugCtx = &m_OutputWindows[id]; } DebugData.outWidth = 0.0f; DebugData.outHeight = 0.0f; DebugData.blitvsSource = GetEmbeddedResource(blit_vert); DebugData.blitfsSource = GetEmbeddedResource(blit_frag); DebugData.blitProg = CreateShaderProgram(DebugData.blitvsSource.c_str(), DebugData.blitfsSource.c_str()); string texfs = GetEmbeddedResource(texdisplay_frag); DebugData.texDisplayProg = CreateShaderProgram(DebugData.blitvsSource.c_str(), texfs.c_str()); string checkerfs = GetEmbeddedResource(checkerboard_frag); DebugData.checkerProg = CreateShaderProgram(DebugData.blitvsSource.c_str(), checkerfs.c_str()); DebugData.genericvsSource = GetEmbeddedResource(generic_vert); DebugData.genericfsSource = GetEmbeddedResource(generic_frag); DebugData.genericProg = CreateShaderProgram(DebugData.genericvsSource.c_str(), DebugData.genericfsSource.c_str()); string meshvs = GetEmbeddedResource(mesh_vert); DebugData.meshProg = CreateShaderProgram(meshvs.c_str(), DebugData.genericfsSource.c_str()); WrappedOpenGL &gl = *m_pDriver; { float data[] = { 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, }; gl.glGenBuffers(1, &DebugData.outlineStripVB); gl.glBindBuffer(eGL_ARRAY_BUFFER, DebugData.outlineStripVB); gl.glBufferData(eGL_ARRAY_BUFFER, sizeof(data), data, eGL_STATIC_DRAW); gl.glGenVertexArrays(1, &DebugData.outlineStripVAO); gl.glBindVertexArray(DebugData.outlineStripVAO); gl.glVertexAttribPointer(0, 4, eGL_FLOAT, false, 0, (const void *)0); gl.glEnableVertexAttribArray(0); } gl.glGenSamplers(1, &DebugData.linearSampler); gl.glSamplerParameteri(DebugData.linearSampler, eGL_TEXTURE_MIN_FILTER, eGL_LINEAR_MIPMAP_NEAREST); gl.glSamplerParameteri(DebugData.linearSampler, eGL_TEXTURE_MAG_FILTER, eGL_LINEAR); gl.glSamplerParameteri(DebugData.linearSampler, eGL_TEXTURE_WRAP_S, eGL_CLAMP_TO_EDGE); gl.glSamplerParameteri(DebugData.linearSampler, eGL_TEXTURE_WRAP_T, eGL_CLAMP_TO_EDGE); gl.glGenSamplers(1, &DebugData.pointSampler); gl.glSamplerParameteri(DebugData.pointSampler, eGL_TEXTURE_MIN_FILTER, eGL_NEAREST_MIPMAP_NEAREST); gl.glSamplerParameteri(DebugData.pointSampler, eGL_TEXTURE_MAG_FILTER, eGL_NEAREST); gl.glSamplerParameteri(DebugData.pointSampler, eGL_TEXTURE_WRAP_S, eGL_CLAMP_TO_EDGE); gl.glSamplerParameteri(DebugData.pointSampler, eGL_TEXTURE_WRAP_T, eGL_CLAMP_TO_EDGE); gl.glGenBuffers(ARRAY_COUNT(DebugData.UBOs), DebugData.UBOs); for(size_t i=0; i < ARRAY_COUNT(DebugData.UBOs); i++) { gl.glBindBuffer(eGL_UNIFORM_BUFFER, DebugData.UBOs[i]); gl.glBufferData(eGL_UNIFORM_BUFFER, DebugData.UBOSize, NULL, eGL_DYNAMIC_DRAW); } DebugData.overlayTexWidth = DebugData.overlayTexHeight = 0; DebugData.overlayTex = DebugData.overlayFBO = 0; gl.glGenFramebuffers(1, &DebugData.pickPixelFBO); gl.glBindFramebuffer(eGL_FRAMEBUFFER, DebugData.pickPixelFBO); gl.glGenTextures(1, &DebugData.pickPixelTex); gl.glBindTexture(eGL_TEXTURE_2D, DebugData.pickPixelTex); gl.glTexStorage2D(eGL_TEXTURE_2D, 1, eGL_RGBA32F, 1, 1); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MIN_FILTER, eGL_NEAREST); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAG_FILTER, eGL_NEAREST); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_WRAP_S, eGL_CLAMP_TO_EDGE); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_WRAP_T, eGL_CLAMP_TO_EDGE); gl.glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, DebugData.pickPixelTex, 0); gl.glGenVertexArrays(1, &DebugData.emptyVAO); gl.glBindVertexArray(DebugData.emptyVAO); MakeCurrentReplayContext(&m_ReplayCtx); gl.glGenVertexArrays(1, &DebugData.meshVAO); gl.glBindVertexArray(DebugData.meshVAO); }
ResourceId GLReplay::RenderOverlay(ResourceId texid, TextureDisplayOverlay overlay, uint32_t frameID, uint32_t eventID) { WrappedOpenGL &gl = *m_pDriver; MakeCurrentReplayContext(&m_ReplayCtx); GLuint curProg = 0; gl.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint*)&curProg); GLuint curDrawFBO = 0; GLuint curReadFBO = 0; gl.glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&curDrawFBO); gl.glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&curReadFBO); auto &progDetails = m_pDriver->m_Programs[m_pDriver->GetResourceManager()->GetID(ProgramRes(curProg))]; if(progDetails.colOutProg == 0) { progDetails.colOutProg = gl.glCreateProgram(); GLuint shad = gl.glCreateShader(eGL_FRAGMENT_SHADER); const char *src = DebugData.genericfsSource.c_str(); gl.glShaderSource(shad, 1, &src, NULL); gl.glCompileShader(shad); gl.glAttachShader(progDetails.colOutProg, shad); gl.glDeleteShader(shad); for(size_t i=0; i < progDetails.shaders.size(); i++) { const auto &shadDetails = m_pDriver->m_Shaders[progDetails.shaders[i]]; if(shadDetails.type != eGL_FRAGMENT_SHADER) { shad = gl.glCreateShader(shadDetails.type); for(size_t s=0; s < shadDetails.sources.size(); s++) { src = shadDetails.sources[s].c_str(); gl.glShaderSource(shad, 1, &src, NULL); } gl.glCompileShader(shad); gl.glAttachShader(progDetails.colOutProg, shad); gl.glDeleteShader(shad); } } gl.glLinkProgram(progDetails.colOutProg); } auto &texDetails = m_pDriver->m_Textures[texid]; if(DebugData.overlayTexWidth != texDetails.width || DebugData.overlayTexHeight != texDetails.height) { if(DebugData.overlayFBO) { gl.glDeleteFramebuffers(1, &DebugData.overlayFBO); gl.glDeleteTextures(1, &DebugData.overlayTex); } gl.glGenFramebuffers(1, &DebugData.overlayFBO); gl.glBindFramebuffer(eGL_FRAMEBUFFER, DebugData.overlayFBO); GLuint curTex = 0; gl.glGetIntegerv(eGL_TEXTURE_BINDING_2D, (GLint*)&curTex); gl.glGenTextures(1, &DebugData.overlayTex); gl.glBindTexture(eGL_TEXTURE_2D, DebugData.overlayTex); DebugData.overlayTexWidth = texDetails.width; DebugData.overlayTexHeight = texDetails.height; gl.glTexStorage2D(eGL_TEXTURE_2D, 1, eGL_RGBA8, texDetails.width, texDetails.height); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MIN_FILTER, eGL_NEAREST); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAG_FILTER, eGL_NEAREST); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_WRAP_S, eGL_CLAMP_TO_EDGE); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_WRAP_T, eGL_CLAMP_TO_EDGE); gl.glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, DebugData.overlayTex, 0); gl.glBindTexture(eGL_TEXTURE_2D, curTex); } gl.glBindFramebuffer(eGL_FRAMEBUFFER, DebugData.overlayFBO); if(overlay == eTexOverlay_NaN || overlay == eTexOverlay_Clipping) { // just need the basic texture float black[] = { 0.0f, 0.0f, 0.0f, 0.0f }; gl.glClearBufferfv(eGL_COLOR, 0, black); } else if(overlay == eTexOverlay_Drawcall) { gl.glUseProgram(progDetails.colOutProg); { // copy across uniforms GLint numUniforms = 0; gl.glGetProgramiv(curProg, eGL_ACTIVE_UNIFORMS, &numUniforms); for(GLint i=0; i < numUniforms; i++) { char uniName[1024] = {}; GLint uniSize = 0; GLenum uniType = eGL_UNKNOWN_ENUM; gl.glGetActiveUniform(curProg, i, 1024, NULL, &uniSize, &uniType, uniName); GLint origloc = gl.glGetUniformLocation(curProg, uniName); GLint newloc = gl.glGetUniformLocation(progDetails.colOutProg, uniName); double dv[16]; float *fv = (float *)dv; if(uniSize > 1) { RDCERR("Array elements beyond [0] not being copied to new program"); } if(origloc != -1 && newloc != -1) { if(uniType == eGL_FLOAT_MAT4) { gl.glGetUniformfv(curProg, origloc, fv); gl.glUniformMatrix4fv(newloc, 1, false, fv); } else if(uniType == eGL_FLOAT_VEC3) { gl.glGetUniformfv(curProg, origloc, fv); gl.glUniform3fv(newloc, 1, fv); } else if(uniType == eGL_FLOAT_VEC4) { gl.glGetUniformfv(curProg, origloc, fv); gl.glUniform4fv(newloc, 1, fv); } else { RDCERR("Uniform type '%s' not being copied to new program", ToStr::Get(uniType).c_str()); } } } } float black[] = { 0.0f, 0.0f, 0.0f, 0.5f }; gl.glClearBufferfv(eGL_COLOR, 0, black); GLint colLoc = gl.glGetUniformLocation(progDetails.colOutProg, "RENDERDOC_GenericFS_Color"); float colVal[] = { 0.8f, 0.1f, 0.8f, 1.0f }; gl.glUniform4fv(colLoc, 1, colVal); ReplayLog(frameID, 0, eventID, eReplay_OnlyDraw); gl.glUseProgram(curProg); } else if(overlay == eTexOverlay_Wireframe) { gl.glUseProgram(progDetails.colOutProg); { // copy across uniforms GLint numUniforms = 0; gl.glGetProgramiv(curProg, eGL_ACTIVE_UNIFORMS, &numUniforms); for(GLint i=0; i < numUniforms; i++) { char uniName[1024] = {}; GLint uniSize = 0; GLenum uniType = eGL_UNKNOWN_ENUM; gl.glGetActiveUniform(curProg, i, 1024, NULL, &uniSize, &uniType, uniName); GLint origloc = gl.glGetUniformLocation(curProg, uniName); GLint newloc = gl.glGetUniformLocation(progDetails.colOutProg, uniName); double dv[16]; float *fv = (float *)dv; if(uniSize > 1) { RDCERR("Array elements beyond [0] not being copied to new program"); } if(origloc != -1 && newloc != -1) { if(uniType == eGL_FLOAT_MAT4) { gl.glGetUniformfv(curProg, origloc, fv); gl.glUniformMatrix4fv(newloc, 1, false, fv); } else if(uniType == eGL_FLOAT_VEC3) { gl.glGetUniformfv(curProg, origloc, fv); gl.glUniform3fv(newloc, 1, fv); } else if(uniType == eGL_FLOAT_VEC4) { gl.glGetUniformfv(curProg, origloc, fv); gl.glUniform4fv(newloc, 1, fv); } else { RDCERR("Uniform type '%s' not being copied to new program", ToStr::Get(uniType).c_str()); } } } } float wireCol[] = { 200.0f/255.0f, 255.0f/255.0f, 0.0f/255.0f, 0.0f }; gl.glClearBufferfv(eGL_COLOR, 0, wireCol); GLint colLoc = gl.glGetUniformLocation(progDetails.colOutProg, "RENDERDOC_GenericFS_Color"); wireCol[3] = 1.0f; gl.glUniform4fv(colLoc, 1, wireCol); GLint depthTest = GL_FALSE; gl.glGetIntegerv(eGL_DEPTH_TEST, (GLint*)&depthTest); GLenum polyMode = eGL_FILL; gl.glGetIntegerv(eGL_POLYGON_MODE, (GLint*)&polyMode); gl.glDisable(eGL_DEPTH_TEST); gl.glPolygonMode(eGL_FRONT_AND_BACK, eGL_LINE); ReplayLog(frameID, 0, eventID, eReplay_OnlyDraw); if(depthTest) gl.glEnable(eGL_DEPTH_TEST); if(polyMode != eGL_LINE) gl.glPolygonMode(eGL_FRONT_AND_BACK, polyMode); gl.glUseProgram(curProg); } gl.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, curDrawFBO); gl.glBindFramebuffer(eGL_READ_FRAMEBUFFER, curReadFBO); return m_pDriver->GetResourceManager()->GetID(TextureRes(DebugData.overlayTex)); }
void GLReplay::FillCBufferVariables(ResourceId shader, uint32_t cbufSlot, vector<ShaderVariable> &outvars, const vector<byte> &data) { WrappedOpenGL &gl = *m_pDriver; MakeCurrentReplayContext(&m_ReplayCtx); GLuint curProg = 0; gl.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint*)&curProg); auto &progDetails = m_pDriver->m_Programs[m_pDriver->GetResourceManager()->GetID(ProgramRes(curProg))]; auto &shaderDetails = m_pDriver->m_Shaders[shader]; GLint numUniforms = 0; gl.glGetProgramInterfaceiv(curProg, eGL_UNIFORM, eGL_ACTIVE_RESOURCES, &numUniforms); const size_t numProps = 6; GLenum resProps[numProps] = { eGL_REFERENCED_BY_VERTEX_SHADER, eGL_TYPE, eGL_NAME_LENGTH, eGL_LOCATION, eGL_BLOCK_INDEX, eGL_ARRAY_SIZE, }; if(shaderDetails.type == eGL_VERTEX_SHADER) resProps[0] = eGL_REFERENCED_BY_VERTEX_SHADER; if(shaderDetails.type == eGL_TESS_CONTROL_SHADER) resProps[0] = eGL_REFERENCED_BY_TESS_CONTROL_SHADER; if(shaderDetails.type == eGL_TESS_EVALUATION_SHADER) resProps[0] = eGL_REFERENCED_BY_TESS_EVALUATION_SHADER; if(shaderDetails.type == eGL_GEOMETRY_SHADER) resProps[0] = eGL_REFERENCED_BY_GEOMETRY_SHADER; if(shaderDetails.type == eGL_FRAGMENT_SHADER) resProps[0] = eGL_REFERENCED_BY_FRAGMENT_SHADER; if(shaderDetails.type == eGL_COMPUTE_SHADER) resProps[0] = eGL_REFERENCED_BY_COMPUTE_SHADER; for(GLint u=0; u < numUniforms; u++) { GLint values[numProps]; gl.glGetProgramResourceiv(curProg, eGL_UNIFORM, u, numProps, resProps, numProps, NULL, values); if(values[0] == GL_FALSE) continue; // don't look at block uniforms just yet if(values[4] != -1) continue; ShaderVariable var; RDCASSERT(values[5] <= 1); // don't handle arrays yet if(values[1] == GL_FLOAT_VEC4) { var.type = eVar_Float; var.columns = 4; var.rows = 1; gl.glGetUniformfv(curProg, values[3], var.value.fv); } else if(values[1] == GL_FLOAT_VEC3) { var.type = eVar_Float; var.columns = 3; var.rows = 1; gl.glGetUniformfv(curProg, values[3], var.value.fv); } else if(values[1] == GL_FLOAT_MAT4) { var.type = eVar_Float; var.columns = 4; var.rows = 4; gl.glGetUniformfv(curProg, values[3], var.value.fv); } else { continue; } create_array_uninit(var.name, values[2]+1); gl.glGetProgramResourceName(curProg, eGL_UNIFORM, u, values[2]+1, NULL, var.name.elems); var.name.count--; // trim off trailing null outvars.push_back(var); } }
void GLReplay::RenderMesh(int frameID, vector<int> eventID, MeshDisplay cfg) { WrappedOpenGL &gl = *m_pDriver; MakeCurrentReplayContext(m_DebugCtx); GLuint curFBO = 0; gl.glGetIntegerv(eGL_FRAMEBUFFER_BINDING, (GLint*)&curFBO); OutputWindow *outw = NULL; for(auto it = m_OutputWindows.begin(); it != m_OutputWindows.end(); ++it) { if(it->second.BlitData.windowFBO == curFBO) { outw = &it->second; break; } } if(!outw) return; const auto &attr = m_CurPipelineState.m_VtxIn.attributes[0]; const auto &vb = m_CurPipelineState.m_VtxIn.vbuffers[attr.BufferSlot]; if(vb.Buffer == ResourceId()) return; MakeCurrentReplayContext(&m_ReplayCtx); GLint viewport[4]; gl.glGetIntegerv(eGL_VIEWPORT, viewport); gl.glGetIntegerv(eGL_FRAMEBUFFER_BINDING, (GLint*)&curFBO); if(outw->BlitData.replayFBO == 0) { gl.glGenFramebuffers(1, &outw->BlitData.replayFBO); gl.glBindFramebuffer(eGL_FRAMEBUFFER, outw->BlitData.replayFBO); gl.glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, outw->BlitData.backbuffer, 0); } else { gl.glBindFramebuffer(eGL_FRAMEBUFFER, outw->BlitData.replayFBO); } gl.glViewport(0, 0, (GLsizei)DebugData.outWidth, (GLsizei)DebugData.outHeight); GLuint curProg = 0; gl.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint*)&curProg); gl.glUseProgram(DebugData.meshProg); float wireCol[] = { 0.0f, 0.0f, 0.0f, 1.0f }; GLint colLoc = gl.glGetUniformLocation(DebugData.meshProg, "RENDERDOC_GenericFS_Color"); gl.glUniform4fv(colLoc, 1, wireCol); Matrix4f projMat = Matrix4f::Perspective(90.0f, 0.1f, 100000.0f, DebugData.outWidth/DebugData.outHeight); Camera cam; if(cfg.arcballCamera) cam.Arcball(cfg.cameraPos.x, Vec3f(cfg.cameraRot.x, cfg.cameraRot.y, cfg.cameraRot.z)); else cam.fpsLook(Vec3f(cfg.cameraPos.x, cfg.cameraPos.y, cfg.cameraPos.z), Vec3f(cfg.cameraRot.x, cfg.cameraRot.y, cfg.cameraRot.z)); Matrix4f camMat = cam.GetMatrix(); Matrix4f ModelViewProj = projMat.Mul(camMat); GLint mvpLoc = gl.glGetUniformLocation(DebugData.meshProg, "ModelViewProj"); gl.glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, ModelViewProj.Data()); GLuint curVAO = 0; gl.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint*)&curVAO); GLuint curArr = 0; gl.glGetIntegerv(eGL_ARRAY_BUFFER_BINDING, (GLint*)&curArr); gl.glBindVertexArray(DebugData.meshVAO); // TODO: we should probably use glBindVertexBuffer, glVertexAttribFormat, glVertexAttribBinding. // For now just assume things about the format and vbuffer. RDCASSERT(attr.Format.compType == eCompType_Float && attr.Format.compByteWidth == 4); gl.glBindBuffer(eGL_ARRAY_BUFFER, m_pDriver->GetResourceManager()->GetLiveResource(vb.Buffer).name); gl.glVertexAttribPointer(0, attr.Format.compCount, eGL_FLOAT, GL_FALSE, 0, (void *)intptr_t(vb.Offset + attr.RelativeOffset)); gl.glEnableVertexAttribArray(0); { GLint depthTest = GL_FALSE; gl.glGetIntegerv(eGL_DEPTH_TEST, (GLint*)&depthTest); GLenum polyMode = eGL_FILL; gl.glGetIntegerv(eGL_POLYGON_MODE, (GLint*)&polyMode); gl.glDisable(eGL_DEPTH_TEST); gl.glPolygonMode(eGL_FRONT_AND_BACK, eGL_LINE); ReplayLog(frameID, 0, eventID[0], eReplay_OnlyDraw); if(depthTest) gl.glEnable(eGL_DEPTH_TEST); if(polyMode != eGL_LINE) gl.glPolygonMode(eGL_FRONT_AND_BACK, polyMode); } gl.glBindVertexArray(curVAO); gl.glBindBuffer(eGL_ARRAY_BUFFER, curArr); gl.glUseProgram(curProg); gl.glViewport(viewport[0], viewport[1], (GLsizei)viewport[2], (GLsizei)viewport[3]); gl.glBindFramebuffer(eGL_FRAMEBUFFER, curFBO); }
FetchTexture GLReplay::GetTexture(ResourceId id) { FetchTexture tex; MakeCurrentReplayContext(&m_ReplayCtx); auto &res = m_pDriver->m_Textures[id]; if(res.resource.Namespace == eResUnknown) { RDCERR("Details for invalid texture id %llu requested", id); RDCEraseEl(tex); return tex; } WrappedOpenGL &gl = *m_pDriver; tex.ID = m_pDriver->GetResourceManager()->GetOriginalID(id); gl.glBindTexture(res.curType, res.resource.name); // if I call this for levels 1, 2, .. etc. Can I get sizes that aren't mip dimensions? GLint width = 1, height = 1, depth = 1, samples=1; gl.glGetTexLevelParameteriv(res.curType, 0, eGL_TEXTURE_WIDTH, &width); gl.glGetTexLevelParameteriv(res.curType, 0, eGL_TEXTURE_HEIGHT, &height); gl.glGetTexLevelParameteriv(res.curType, 0, eGL_TEXTURE_DEPTH, &depth); gl.glGetTexLevelParameteriv(res.curType, 0, eGL_TEXTURE_SAMPLES, &samples); if(res.width == 0) { RDCWARN("TextureData::width didn't get filled out, setting at last minute"); res.width = width; } if(res.height == 0) { RDCWARN("TextureData::height didn't get filled out, setting at last minute"); res.height = height; } if(res.depth == 0) { RDCWARN("TextureData::depth didn't get filled out, setting at last minute"); res.depth = depth; } // reasonably common defaults tex.msQual = 0; tex.msSamp = 1; tex.width = tex.height = tex.depth = tex.arraysize = 1; tex.cubemap = false; switch(res.curType) { case eGL_TEXTURE_1D: case eGL_TEXTURE_BUFFER: tex.dimension = 1; tex.width = (uint32_t)width; break; case eGL_TEXTURE_1D_ARRAY: tex.dimension = 1; tex.width = (uint32_t)width; tex.arraysize = depth; break; case eGL_TEXTURE_2D: case eGL_TEXTURE_RECTANGLE: case eGL_TEXTURE_2D_MULTISAMPLE: case eGL_TEXTURE_CUBE_MAP: tex.dimension = 2; tex.width = (uint32_t)width; tex.height = (uint32_t)height; tex.cubemap = (res.curType == eGL_TEXTURE_CUBE_MAP); tex.msSamp = (res.curType == eGL_TEXTURE_2D_MULTISAMPLE ? samples : 1); break; case eGL_TEXTURE_2D_ARRAY: case eGL_TEXTURE_2D_MULTISAMPLE_ARRAY: case eGL_TEXTURE_CUBE_MAP_ARRAY: tex.dimension = 2; tex.width = (uint32_t)width; tex.height = (uint32_t)height; tex.arraysize = depth; tex.cubemap = (res.curType == eGL_TEXTURE_CUBE_MAP_ARRAY); tex.msSamp = (res.curType == eGL_TEXTURE_2D_MULTISAMPLE_ARRAY ? samples : 1); break; case eGL_TEXTURE_3D: tex.dimension = 3; tex.width = (uint32_t)width; tex.height = (uint32_t)height; tex.depth = (uint32_t)depth; break; default: tex.dimension = 2; RDCERR("Unexpected texture enum %hs", ToStr::Get(res.curType).c_str()); } GLint immut = 0; gl.glGetTexParameteriv(res.curType, eGL_TEXTURE_IMMUTABLE_FORMAT, &immut); if(immut) { gl.glGetTexParameteriv(res.curType, eGL_TEXTURE_IMMUTABLE_LEVELS, &immut); tex.mips = (uint32_t)immut; } else { // assuming complete texture GLint mips = 1; gl.glGetTexParameteriv(res.curType, eGL_TEXTURE_MAX_LEVEL, &mips); tex.mips = (uint32_t)mips; } tex.numSubresources = tex.mips*tex.arraysize; // surely this will be the same for each level... right? that would be insane if it wasn't GLint fmt = 0; gl.glGetTexLevelParameteriv(res.curType, 0, eGL_TEXTURE_INTERNAL_FORMAT, &fmt); tex.format = MakeResourceFormat(gl, res.curType, (GLenum)fmt); string str = ""; char name[128] = {0}; gl.glGetObjectLabel(eGL_TEXTURE, res.resource.name, 127, NULL, name); str = name; tex.customName = true; if(str == "") { tex.customName = false; str = StringFormat::Fmt("Texture%dD %llu", tex.dimension, tex.ID); } tex.name = widen(str); tex.creationFlags = eTextureCreate_SRV; if(tex.format.compType == eCompType_Depth) tex.creationFlags |= eTextureCreate_DSV; GLNOTIMP("creationFlags are not calculated yet"); tex.byteSize = 0; GLNOTIMP("Not calculating bytesize"); return tex; }
uint64_t GLReplay::MakeOutputWindow(void *wn, bool depth) { void **displayAndDrawable = (void **)wn; Display *dpy = NULL; GLXDrawable wnd = 0; if(wn) { dpy = (Display *)displayAndDrawable[0]; wnd = (GLXDrawable)displayAndDrawable[1]; } else { dpy = XOpenDisplay(NULL); if(dpy == NULL) return 0; } static int visAttribs[] = { GLX_X_RENDERABLE, True, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8, GLX_DOUBLEBUFFER, True, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, True, 0 }; int numCfgs = 0; GLXFBConfig *fbcfg = glXChooseFBConfigProc(dpy, DefaultScreen(dpy), visAttribs, &numCfgs); if(fbcfg == NULL) { XCloseDisplay(dpy); RDCERR("Couldn't choose default framebuffer config"); return eReplayCreate_APIInitFailed; } int attribs[64] = {0}; int i=0; attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB; attribs[i++] = 4; attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB; attribs[i++] = 3; attribs[i++] = GLX_CONTEXT_FLAGS_ARB; attribs[i++] = GLX_CONTEXT_DEBUG_BIT_ARB; attribs[i++] = GLX_CONTEXT_PROFILE_MASK_ARB; attribs[i++] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; GLXContext ctx = glXCreateContextAttribsProc(dpy, fbcfg[0], m_ReplayCtx.ctx, true, attribs); if(ctx == NULL) { XCloseDisplay(dpy); RDCERR("Couldn't create 4.3 context - RenderDoc requires OpenGL 4.3 availability"); return 0; } if(wnd == 0) { // don't care about pbuffer properties as we won't render directly to this int pbAttribs[] = { GLX_PBUFFER_WIDTH, 32, GLX_PBUFFER_HEIGHT, 32, 0 }; wnd = glXCreatePbufferProc(dpy, fbcfg[0], pbAttribs); } XFree(fbcfg); OutputWindow win; win.dpy = dpy; win.ctx = ctx; win.wnd = wnd; glXQueryDrawableProc(dpy, wnd, GLX_WIDTH, (unsigned int *)&win.width); glXQueryDrawableProc(dpy, wnd, GLX_HEIGHT, (unsigned int *)&win.height); MakeCurrentReplayContext(&win); InitOutputWindow(win); CreateOutputWindowBackbuffer(win, depth); uint64_t ret = m_OutputWindowID++; m_OutputWindows[ret] = win; return ret; }
FetchBuffer GLReplay::GetBuffer(ResourceId id) { FetchBuffer ret; MakeCurrentReplayContext(&m_ReplayCtx); auto &res = m_pDriver->m_Buffers[id]; if(res.resource.Namespace == eResUnknown) { RDCERR("Details for invalid buffer id %llu requested", id); RDCEraseEl(ret); return ret; } WrappedOpenGL &gl = *m_pDriver; ret.ID = m_pDriver->GetResourceManager()->GetOriginalID(id); gl.glBindBuffer(res.curType, res.resource.name); ret.structureSize = 0; GLNOTIMP("Not fetching structure size (if there's an equivalent)"); ret.creationFlags = 0; switch(res.curType) { case eGL_ARRAY_BUFFER: ret.creationFlags = eBufferCreate_VB; break; case eGL_ELEMENT_ARRAY_BUFFER: ret.creationFlags = eBufferCreate_IB; break; default: RDCERR("Unexpected buffer type %hs", ToStr::Get(res.curType).c_str()); } GLint size; gl.glGetBufferParameteriv(res.curType, eGL_BUFFER_SIZE, &size); ret.byteSize = ret.length = (uint32_t)size; if(res.size == 0) { RDCWARN("BufferData::size didn't get filled out, setting at last minute"); res.size = ret.byteSize; } string str = ""; char name[128] = {0}; gl.glGetObjectLabel(eGL_BUFFER, res.resource.name, 127, NULL, name); str = name; ret.customName = true; if(str == "") { ret.customName = false; str = StringFormat::Fmt("Buffer %llu", ret.ID); } ret.name = widen(str); return ret; }