Files
renderdoc/renderdoc/driver/gl/gl_driver.cpp
T

4506 lines
129 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2015-2016 Baldur Karlsson
* Copyright (c) 2014 Crytek
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "common/common.h"
#include "gl_driver.h"
#include "driver/shaders/spirv/spirv_common.h"
#include "serialise/string_utils.h"
#include "replay/type_helpers.h"
#include "maths/vec.h"
#include "jpeg-compressor/jpge.h"
#include "stb/stb_truetype.h"
#include "data/glsl/debuguniforms.h"
#include <algorithm>
const int firstChar = int(' ') + 1;
const int lastChar = 127;
const int numChars = lastChar-firstChar;
const float charPixelHeight = 20.0f;
stbtt_bakedchar chardata[numChars];
const char *GLChunkNames[] =
{
"WrappedOpenGL::Initialisation",
"glGenTextures",
"glCreateTextures",
"glBindTexture",
"glBindTextures",
"glBindMultiTexture",
"glBindTextureUnit",
"glBindImageTexture",
"glBindImageTextures",
"glActiveTexture",
"glTexStorage1D",
"glTexStorage2D",
"glTexStorage3D",
"glTexStorage2DMultisample",
"glTexStorage3DMultisample",
"glTexImage1D",
"glTexImage2D",
"glTexImage3D",
"glTexSubImage1D",
"glTexSubImage2D",
"glTexSubImage3D",
"glCompressedTexImage1D",
"glCompressedTexImage2D",
"glCompressedTexImage3D",
"glCompressedTexSubImage1D",
"glCompressedTexSubImage2D",
"glCompressedTexSubImage3D",
"glTexBuffer",
"glTexBufferRange",
"glPixelStore",
"glTexParameterf",
"glTexParameterfv",
"glTexParameteri",
"glTexParameteriv",
"glTexParameterIiv",
"glTexParameterIuiv",
"glGenerateMipmap",
"glCopyImageSubData",
"glCopyTexImage1D",
"glCopyTexImage2D",
"glCopyTexSubImage1D",
"glCopyTexSubImage2D",
"glCopyTexSubImage3D",
"glTextureView",
"glCreateShader",
"glCreateProgram",
"glCreateShaderProgramv",
"glCompileShader",
"glShaderSource",
"glAttachShader",
"glDetachShader",
"glUseProgram",
"glProgramParameter",
"glTransformFeedbackVaryings",
"glBindAttribLocation",
"glBindFragDataLocation",
"glBindFragDataLocationIndexed",
"glUniformBlockBinding",
"glShaderStorageBlockBinding",
"glUniformSubroutinesuiv",
"glProgramUniformVector*",
"glProgramUniformMatrix*",
"glLinkProgram",
"glNamedStringARB",
"glDeleteNamedStringARB",
"glCompileShaderIncludeARB",
"glGenTransformFeedbacks",
"glCreateTransformFeedbacks",
"glBindTransformFeedback",
"glBeginTransformFeedback",
"glEndTransformFeedback",
"glPauseTransformFeedback",
"glResumeTransformFeedback",
"glGenProgramPipelines",
"glCreateProgramPipelines",
"glUseProgramStages",
"glBindProgramPipeline",
"glFenceSync",
"glClientWaitSync",
"glWaitSync",
"glGenQueries",
"glCreateQueries",
"glBeginQuery",
"glBeginQueryIndexed",
"glEndQuery",
"glEndQueryIndexed",
"glBeginConditional",
"glEndConditional",
"glQueryCounter",
"glClearColor",
"glClearDepth",
"glClearStencil",
"glClear",
"glClearBufferfv",
"glClearBufferiv",
"glClearBufferuiv",
"glClearBufferfi",
"glClearBufferData",
"glClearBufferSubData",
"glClearTexImage",
"glClearTexSubImage",
"glPolygonMode",
"glPolygonOffset",
"glPolygonOffsetClampEXT",
"glCullFace",
"glHint",
"glEnable",
"glDisable",
"glEnablei",
"glDisablei",
"glFrontFace",
"glBlendFunc",
"glBlendFunci",
"glBlendColor",
"glBlendFuncSeparate",
"glBlendFuncSeparatei",
"glBlendEquation",
"glBlendEquationi",
"glBlendEquationSeparate",
"glBlendEquationSeparatei",
"glBlendBarrierKHR",
"glLogicOp",
"glStencilOp",
"glStencilOpSeparate",
"glStencilFunc",
"glStencilFuncSeparate",
"glStencilMask",
"glStencilMaskSeparate",
"glColorMask",
"glColorMaski",
"glSampleMaski",
"glSampleCoverage",
"glMinSampleShading",
"glRasterSamplesEXT",
"glDepthFunc",
"glDepthMask",
"glDepthRange",
"glDepthRangef",
"glDepthRangeIndexed",
"glDepthRangeArrayv",
"glDepthBounds",
"glClipControl",
"glProvokingVertex",
"glPrimitiveRestartIndex",
"glPatchParameteri",
"glPatchParameterfv",
"glLineWidth",
"glPointSize",
"glPointParameterf",
"glPointParameterfv",
"glPointParameteri",
"glPointParameteriv",
"glViewport",
"glViewportArrayv",
"glScissor",
"glScissorArrayv",
"glBindVertexBuffer",
"glBindVertexBuffers",
"glVertexBindingDivisor",
"glDispatchCompute",
"glDispatchComputeGroupSizeARB",
"glDispatchComputeIndirect",
"glMemoryBarrier",
"glMemoryBarrierByRegion",
"glTextureBarrier",
"glDrawArrays",
"glDrawArraysIndirect",
"glDrawArraysInstanced",
"glDrawArraysInstancedBaseInstance",
"glDrawElements",
"glDrawElementsIndirect",
"glDrawRangeElements",
"glDrawRangeElementsBaseVertex",
"glDrawElementsInstanced",
"glDrawElementsInstancedBaseInstance",
"glDrawElementsBaseVertex",
"glDrawElementsInstancedBaseVertex",
"glDrawElementsInstancedBaseVertexBaseInstance",
"glDrawTransformFeedback",
"glDrawTransformFeedbackInstanced",
"glDrawTransformFeedbackStream",
"glDrawTransformFeedbackStreamInstanced",
"glMultiDrawArrays",
"glMultiDrawElements",
"glMultiDrawElementsBaseVertex",
"glMultiDrawArraysIndirect",
"glMultiDrawElementsIndirect",
"glMultiDrawArraysIndirectCountARB",
"glMultiDrawElementsIndirectCountARB",
"glGenFramebuffers",
"glCreateFramebuffers",
"glFramebufferTexture",
"glFramebufferTexture1D",
"glFramebufferTexture2D",
"glFramebufferTexture3D",
"glFramebufferRenderbuffer",
"glFramebufferTextureLayer",
"glFramebufferParameteri",
"glReadBuffer",
"glBindFramebuffer",
"glDrawBuffer",
"glDrawBuffers",
"glBlitFramebuffer",
"glGenRenderbuffers",
"glCreateRenderbuffers",
"glRenderbufferStorage",
"glRenderbufferStorageMultisample",
"glGenSamplers",
"glCreateSamplers",
"glSamplerParameteri",
"glSamplerParameterf",
"glSamplerParameteriv",
"glSamplerParameterfv",
"glSamplerParameterIiv",
"glSamplerParameterIuiv",
"glBindSampler",
"glBindSamplers",
"glGenBuffers",
"glCreateBuffers",
"glBindBuffer",
"glBindBufferBase",
"glBindBufferRange",
"glBindBuffersBase",
"glBindBuffersRange",
"glBufferStorage",
"glBufferData",
"glBufferSubData",
"glCopyBufferSubData",
"glUnmapBuffer",
"glFlushMappedBufferRange",
"glGenVertexArrays",
"glCreateVertexArrays",
"glBindVertexArray",
"glVertexAttrib*",
"glVertexAttribPointer",
"glVertexAttribIPointer",
"glVertexAttribLPointer",
"glEnableVertexAttribArray",
"glDisableVertexAttribArray",
"glVertexAttribFormat",
"glVertexAttribIFormat",
"glVertexAttribLFormat",
"glVertexAttribDivisor",
"glVertexAttribBinding",
"glVertexArrayElementBuffer",
"glTransformFeedbackBufferBase",
"glTransformFeedbackBufferRange",
"glObjectLabel",
"glPushDebugGroup",
"glDebugMessageInsert",
"glPopDebugGroup",
"DebugMessageList",
"Capture",
"BeginCapture",
"EndCapture",
};
GLInitParams::GLInitParams()
{
SerialiseVersion = GL_SERIALISE_VERSION;
colorBits = 32;
depthBits = 32;
stencilBits = 8;
isSRGB = 1;
multiSamples = 1;
width = 32;
height = 32;
}
// handling for these versions is scattered throughout the code (as relevant to enable/disable bits of serialisation
// and set some defaults if necessary).
// Here we list which non-current versions we support, and what changed
const uint32_t GLInitParams::GL_OLD_VERSIONS[GLInitParams::GL_NUM_SUPPORTED_OLD_VERSIONS] = {
0x000010, // from 0x10 to 0x11, we added a dummy marker value used to identify serialised data in glUseProgramStages (hack :( )
};
ReplayCreateStatus GLInitParams::Serialise()
{
SERIALISE_ELEMENT(uint32_t, ver, GL_SERIALISE_VERSION); SerialiseVersion = ver;
if(ver != GL_SERIALISE_VERSION)
{
bool oldsupported = false;
for(uint32_t i=0; i < GL_NUM_SUPPORTED_OLD_VERSIONS; i++)
{
if(ver == GL_OLD_VERSIONS[i])
{
oldsupported = true;
RDCWARN("Old OpenGL serialise version %d, latest is %d. Loading with possibly degraded features/support.", ver, GL_SERIALISE_VERSION);
}
}
if(!oldsupported)
{
RDCERR("Incompatible OpenGL serialise version, expected %d got %d", GL_SERIALISE_VERSION, ver);
return eReplayCreate_APIIncompatibleVersion;
}
}
m_pSerialiser->Serialise("Color bits", colorBits);
m_pSerialiser->Serialise("Depth bits", depthBits);
m_pSerialiser->Serialise("Stencil bits", stencilBits);
m_pSerialiser->Serialise("Is SRGB", isSRGB);
m_pSerialiser->Serialise("MSAA samples", multiSamples);
m_pSerialiser->Serialise("Width", width);
m_pSerialiser->Serialise("Height", height);
return eReplayCreate_Success;
}
WrappedOpenGL::WrappedOpenGL(const char *logfile, const GLHookSet &funcs)
: m_Real(funcs)
{
if(RenderDoc::Inst().GetCrashHandler())
RenderDoc::Inst().GetCrashHandler()->RegisterMemoryRegion(this, sizeof(WrappedOpenGL));
globalExts.push_back("GL_ARB_arrays_of_arrays");
globalExts.push_back("GL_ARB_base_instance");
globalExts.push_back("GL_ARB_blend_func_extended");
globalExts.push_back("GL_ARB_buffer_storage");
globalExts.push_back("GL_ARB_clear_buffer_object");
globalExts.push_back("GL_ARB_clear_texture");
globalExts.push_back("GL_ARB_clip_control");
globalExts.push_back("GL_ARB_color_buffer_float");
globalExts.push_back("GL_ARB_compressed_texture_pixel_storage");
globalExts.push_back("GL_ARB_compute_shader");
globalExts.push_back("GL_ARB_compute_variable_group_size");
globalExts.push_back("GL_ARB_conditional_render_inverted");
globalExts.push_back("GL_ARB_conservative_depth");
globalExts.push_back("GL_ARB_copy_buffer");
globalExts.push_back("GL_ARB_copy_image");
globalExts.push_back("GL_ARB_cull_distance");
globalExts.push_back("GL_ARB_debug_output");
globalExts.push_back("GL_ARB_depth_buffer_float");
globalExts.push_back("GL_ARB_depth_clamp");
globalExts.push_back("GL_ARB_depth_texture");
globalExts.push_back("GL_ARB_derivative_control");
globalExts.push_back("GL_ARB_direct_state_access");
globalExts.push_back("GL_ARB_draw_buffers");
globalExts.push_back("GL_ARB_draw_buffers_blend");
globalExts.push_back("GL_ARB_draw_elements_base_vertex");
globalExts.push_back("GL_ARB_draw_indirect");
globalExts.push_back("GL_ARB_draw_instanced");
globalExts.push_back("GL_ARB_enhanced_layouts");
globalExts.push_back("GL_ARB_ES2_compatibility");
globalExts.push_back("GL_ARB_ES3_1_compatibility");
globalExts.push_back("GL_ARB_ES3_compatibility");
globalExts.push_back("GL_ARB_explicit_attrib_location");
globalExts.push_back("GL_ARB_explicit_uniform_location");
globalExts.push_back("GL_ARB_fragment_coord_conventions");
globalExts.push_back("GL_ARB_fragment_layer_viewport");
globalExts.push_back("GL_ARB_fragment_shader_interlock");
globalExts.push_back("GL_ARB_framebuffer_no_attachments");
globalExts.push_back("GL_ARB_framebuffer_object");
globalExts.push_back("GL_ARB_framebuffer_sRGB");
globalExts.push_back("GL_ARB_geometry_shader4");
globalExts.push_back("GL_ARB_get_program_binary");
globalExts.push_back("GL_ARB_get_texture_sub_image");
globalExts.push_back("GL_ARB_gpu_shader_fp64");
globalExts.push_back("GL_ARB_gpu_shader5");
globalExts.push_back("GL_ARB_half_float_pixel");
globalExts.push_back("GL_ARB_half_float_vertex");
globalExts.push_back("GL_ARB_indirect_parameters");
globalExts.push_back("GL_ARB_instanced_arrays");
globalExts.push_back("GL_ARB_internalformat_query");
globalExts.push_back("GL_ARB_internalformat_query2");
globalExts.push_back("GL_ARB_invalidate_subdata");
globalExts.push_back("GL_ARB_map_buffer_alignment");
globalExts.push_back("GL_ARB_map_buffer_range");
globalExts.push_back("GL_ARB_multi_bind");
globalExts.push_back("GL_ARB_multi_draw_indirect");
globalExts.push_back("GL_ARB_multisample");
globalExts.push_back("GL_ARB_multitexture");
globalExts.push_back("GL_ARB_occlusion_query");
globalExts.push_back("GL_ARB_occlusion_query2");
globalExts.push_back("GL_ARB_pixel_buffer_object");
globalExts.push_back("GL_ARB_pipeline_statistics_query");
globalExts.push_back("GL_ARB_point_parameters");
globalExts.push_back("GL_ARB_point_sprite");
globalExts.push_back("GL_ARB_post_depth_coverage");
globalExts.push_back("GL_ARB_program_interface_query");
globalExts.push_back("GL_ARB_provoking_vertex");
globalExts.push_back("GL_ARB_query_buffer_object");
globalExts.push_back("GL_ARB_robust_buffer_access_behavior");
globalExts.push_back("GL_ARB_robustness");
globalExts.push_back("GL_ARB_robustness_application_isolation");
globalExts.push_back("GL_ARB_robustness_share_group_isolation");
globalExts.push_back("GL_ARB_sample_shading");
globalExts.push_back("GL_ARB_sampler_objects");
globalExts.push_back("GL_ARB_seamless_cube_map");
globalExts.push_back("GL_ARB_seamless_cubemap_per_texture");
globalExts.push_back("GL_ARB_separate_shader_objects");
globalExts.push_back("GL_ARB_shader_atomic_counters");
globalExts.push_back("GL_ARB_shader_atomic_counter_ops");
globalExts.push_back("GL_ARB_shader_ballot");
globalExts.push_back("GL_ARB_shader_bit_encoding");
globalExts.push_back("GL_ARB_shader_clock");
globalExts.push_back("GL_ARB_shader_draw_parameters");
globalExts.push_back("GL_ARB_shader_group_vote");
globalExts.push_back("GL_ARB_shader_image_load_store");
globalExts.push_back("GL_ARB_shader_image_size");
globalExts.push_back("GL_ARB_shader_precision");
globalExts.push_back("GL_ARB_shader_stencil_export");
globalExts.push_back("GL_ARB_shader_storage_buffer_object");
globalExts.push_back("GL_ARB_shader_subroutine");
globalExts.push_back("GL_ARB_shader_texture_image_samples");
globalExts.push_back("GL_ARB_shader_texture_lod");
globalExts.push_back("GL_ARB_shader_viewport_layer_array");
globalExts.push_back("GL_ARB_shading_language_100");
globalExts.push_back("GL_ARB_shading_language_420pack");
globalExts.push_back("GL_ARB_shading_language_include");
globalExts.push_back("GL_ARB_shading_language_packing");
globalExts.push_back("GL_ARB_shadow");
globalExts.push_back("GL_ARB_shadow_ambient");
globalExts.push_back("GL_ARB_stencil_texturing");
globalExts.push_back("GL_ARB_sync");
globalExts.push_back("GL_ARB_tessellation_shader");
globalExts.push_back("GL_ARB_texture_barrier");
globalExts.push_back("GL_ARB_texture_border_clamp");
globalExts.push_back("GL_ARB_texture_buffer_object");
globalExts.push_back("GL_ARB_texture_buffer_object_rgb32");
globalExts.push_back("GL_ARB_texture_buffer_range");
globalExts.push_back("GL_ARB_texture_compression");
globalExts.push_back("GL_ARB_texture_compression_bptc");
globalExts.push_back("GL_ARB_texture_compression_rgtc");
globalExts.push_back("GL_ARB_texture_cube_map");
globalExts.push_back("GL_ARB_texture_cube_map_array");
globalExts.push_back("GL_ARB_texture_float");
globalExts.push_back("GL_ARB_texture_gather");
globalExts.push_back("GL_ARB_texture_mirror_clamp_to_edge");
globalExts.push_back("GL_ARB_texture_mirrored_repeat");
globalExts.push_back("GL_ARB_texture_multisample");
globalExts.push_back("GL_ARB_texture_non_power_of_two");
globalExts.push_back("GL_ARB_texture_query_levels");
globalExts.push_back("GL_ARB_texture_query_lod");
globalExts.push_back("GL_ARB_texture_rectangle");
globalExts.push_back("GL_ARB_texture_rg");
globalExts.push_back("GL_ARB_texture_rgb10_a2ui");
globalExts.push_back("GL_ARB_texture_stencil8");
globalExts.push_back("GL_ARB_texture_storage");
globalExts.push_back("GL_ARB_texture_storage_multisample");
globalExts.push_back("GL_ARB_texture_swizzle");
globalExts.push_back("GL_ARB_texture_view");
globalExts.push_back("GL_ARB_timer_query");
globalExts.push_back("GL_ARB_transform_feedback_instanced");
globalExts.push_back("GL_ARB_transform_feedback_overflow_query");
globalExts.push_back("GL_ARB_transform_feedback2");
globalExts.push_back("GL_ARB_transform_feedback3");
globalExts.push_back("GL_ARB_uniform_buffer_object");
globalExts.push_back("GL_ARB_vertex_array_bgra");
globalExts.push_back("GL_ARB_vertex_array_object");
globalExts.push_back("GL_ARB_vertex_attrib_64bit");
globalExts.push_back("GL_ARB_vertex_attrib_binding");
globalExts.push_back("GL_ARB_vertex_buffer_object");
globalExts.push_back("GL_ARB_vertex_program");
globalExts.push_back("GL_ARB_vertex_type_10f_11f_11f_rev");
globalExts.push_back("GL_ARB_vertex_type_2_10_10_10_rev");
globalExts.push_back("GL_ARB_viewport_array");
globalExts.push_back("GL_EXT_bgra");
globalExts.push_back("GL_EXT_blend_color");
globalExts.push_back("GL_EXT_blend_equation_separate");
globalExts.push_back("GL_EXT_blend_func_separate");
globalExts.push_back("GL_EXT_blend_minmax");
globalExts.push_back("GL_EXT_blend_subtract");
globalExts.push_back("GL_EXT_debug_label");
globalExts.push_back("GL_EXT_debug_marker");
globalExts.push_back("GL_EXT_depth_bounds_test");
globalExts.push_back("GL_EXT_direct_state_access");
globalExts.push_back("GL_EXT_draw_buffers2");
globalExts.push_back("GL_EXT_draw_instanced");
globalExts.push_back("GL_EXT_draw_range_elements");
globalExts.push_back("GL_EXT_framebuffer_blit");
globalExts.push_back("GL_EXT_framebuffer_multisample");
globalExts.push_back("GL_EXT_framebuffer_multisample_blit_scaled");
globalExts.push_back("GL_EXT_framebuffer_object");
globalExts.push_back("GL_EXT_framebuffer_sRGB");
globalExts.push_back("GL_EXT_gpu_shader4");
globalExts.push_back("GL_EXT_multisample");
globalExts.push_back("GL_EXT_multi_draw_arrays");
globalExts.push_back("GL_EXT_packed_depth_stencil");
globalExts.push_back("GL_EXT_packed_float");
globalExts.push_back("GL_EXT_pixel_buffer_object");
globalExts.push_back("GL_EXT_pixel_buffer_object");
globalExts.push_back("GL_EXT_point_parameters");
globalExts.push_back("GL_EXT_polygon_offset_clamp");
globalExts.push_back("GL_EXT_post_depth_coverage");
globalExts.push_back("GL_EXT_provoking_vertex");
globalExts.push_back("GL_EXT_raster_multisample");
globalExts.push_back("GL_EXT_shader_image_load_store");
globalExts.push_back("GL_EXT_shader_image_load_formatted");
globalExts.push_back("GL_EXT_shader_integer_mix");
globalExts.push_back("GL_EXT_shadow_funcs");
globalExts.push_back("GL_EXT_stencil_wrap");
globalExts.push_back("GL_EXT_texture_array");
globalExts.push_back("GL_EXT_texture_buffer_object");
globalExts.push_back("GL_EXT_texture_compression_dxt1");
globalExts.push_back("GL_EXT_texture_compression_rgtc");
globalExts.push_back("GL_EXT_texture_compression_s3tc");
globalExts.push_back("GL_EXT_texture_cube_map");
globalExts.push_back("GL_EXT_texture_edge_clamp");
globalExts.push_back("GL_EXT_texture_filter_anisotropic");
globalExts.push_back("GL_EXT_texture_filter_minmax");
globalExts.push_back("GL_EXT_texture_integer");
globalExts.push_back("GL_EXT_texture_lod_bias");
globalExts.push_back("GL_EXT_texture_mirror_clamp");
globalExts.push_back("GL_EXT_texture_shared_exponent");
globalExts.push_back("GL_EXT_texture_snorm");
globalExts.push_back("GL_EXT_texture_sRGB");
globalExts.push_back("GL_EXT_texture_sRGB_decode");
globalExts.push_back("GL_EXT_texture_swizzle");
globalExts.push_back("GL_EXT_texture3D");
globalExts.push_back("GL_EXT_timer_query");
globalExts.push_back("GL_EXT_transform_feedback");
globalExts.push_back("GL_EXT_vertex_attrib_64bit");
globalExts.push_back("GL_GREMEDY_frame_terminator");
globalExts.push_back("GL_GREMEDY_string_marker");
globalExts.push_back("GL_KHR_blend_equation_advanced");
globalExts.push_back("GL_KHR_blend_equation_advanced_coherent");
globalExts.push_back("GL_KHR_context_flush_control");
globalExts.push_back("GL_KHR_debug");
globalExts.push_back("GL_KHR_no_error");
globalExts.push_back("GL_KHR_robustness");
globalExts.push_back("GL_KHR_robust_buffer_access_behavior");
// this WGL extension is advertised in the gl ext string instead of via the wgl ext string,
// return it just in case anyone is checking for it via this place. On non-windows platforms
// it won't be reported as we do the intersection of renderdoc supported extensions and
// implementation supported extensions.
globalExts.push_back("WGL_EXT_swap_control");
/************************************************************************
Extensions I plan to support, but haven't implemented yet for one reason or another.
Usually complexity/time considerations.
Vendor specific extensions aren't listed here, or below in the 'will never support' list.
Only very important/commonly used vendor extensions will be supported, generally I'll
stick to ARB, EXT and KHR.
* GL_ARB_bindless_texture
* GL_ARB_cl_event
* GL_ARB_sparse_buffer
* GL_ARB_sparse_texture
* GL_EXT_sparse_texture2
* GL_ARB_sparse_texture2
* GL_ARB_sparse_texture_clamp <- this one is free, but no point exposing until other spares exts
* GL_EXT_x11_sync_object
* GL_KHR_texture_compression_astc_hdr <- without support for astc textures on PC hardware this
* GL_KHR_texture_compression_astc_ldr <- could be difficult. Maybe falls into the category of 'only
support if it's supported on replaying driver'?
* GL_ARB_ES3_2_compatibility
* GL_ARB_gpu_shader_int64
* GL_ARB_parallel_shader_compile
* GL_ARB_sample_locations
* GL_ARB_texture_filter_minmax
************************************************************************/
/************************************************************************
Extensions I never plan to support due to only referring to old/outdated functionality listed below.
I'm not sure what to do about GL_ARB_imaging, it seems like it's somewhat used in modern GL? For now
I'm hoping I can get away with not reporting it but implementing the functionality it still describes.
* GL_ARB_compatibility
* GL_ARB_fragment_program
* GL_ARB_fragment_program_shadow
* GL_ARB_fragment_shader
* GL_ARB_matrix_palette
* GL_ARB_shader_objects
* GL_ARB_texture_env_add
* GL_ARB_texture_env_combine
* GL_ARB_texture_env_crossbar
* GL_ARB_texture_env_dot3
* GL_ARB_transpose_matrix
* GL_ARB_vertex_blend
* GL_ARB_vertex_program
* GL_ARB_vertex_shader
* GL_ARB_window_pos
* GL_ATI_draw_buffers
* GL_ATI_texture_float
* GL_ATI_texture_mirror_once
* GL_EXT_422_pixels
* GL_EXT_abgr
* GL_EXT_bindable_uniform
* GL_EXT_blend_logic_op
* GL_EXT_Cg_shader
* GL_EXT_clip_volume_hint
* GL_EXT_cmyka
* GL_EXT_color_subtable
* GL_EXT_compiled_vertex_array
* GL_EXT_convolution
* GL_EXT_coordinate_frame
* GL_EXT_copy_texture
* GL_EXT_cull_vertex
* GL_EXT_fog_coord
* GL_EXT_fragment_lighting
* GL_EXT_geometry_shader4
* GL_EXT_gpu_program_parameters
* GL_EXT_histogram
* GL_EXT_import_sync_object
* GL_EXT_index_array_formats
* GL_EXT_index_func
* GL_EXT_index_material
* GL_EXT_index_texture
* GL_EXT_light_texture
* GL_EXT_misc_attribute
* GL_EXT_packed_pixels
* GL_EXT_paletted_texture
* GL_EXT_pixel_transform
* GL_EXT_pixel_transform_color_table
* GL_EXT_rescale_normal
* GL_EXT_scene_marker
* GL_EXT_secondary_color
* GL_EXT_separate_shader_objects
* GL_EXT_separate_specular_color
* GL_EXT_shared_texture_palette
* GL_EXT_stencil_clear_tag
* GL_EXT_stencil_two_side
* GL_EXT_subtexture
* GL_EXT_texture_compression_latc
* GL_EXT_texture_env_add
* GL_EXT_texture_env_combine
* GL_EXT_texture_env_dot3
* GL_EXT_texture_lod
* GL_EXT_texture_object
* GL_EXT_texture_perturb_normal
* GL_EXT_texture_storage
* GL_EXT_vertex_array
* GL_EXT_vertex_array_bgra
* GL_EXT_vertex_shader
* GL_EXT_vertex_weighting
* GL_S3_s3tc
************************************************************************/
// we'll be sorting the implementation extension array, so make sure the
// sorts are identical so we can do the intersection easily
std::sort(globalExts.begin(), globalExts.end());
m_Replay.SetDriver(this);
m_FrameCounter = 0;
m_FailedFrame = 0;
m_FailedReason = CaptureSucceeded;
m_Failures = 0;
m_SuccessfulCapture = true;
m_FailureReason = CaptureSucceeded;
m_FrameTimer.Restart();
m_AppControlledCapture = false;
m_TotalTime = m_AvgFrametime = m_MinFrametime = m_MaxFrametime = 0.0;
m_RealDebugFunc = NULL;
m_RealDebugFuncParam = NULL;
m_DrawcallStack.push_back(&m_ParentDrawcall);
m_CurEventID = 0;
m_CurDrawcallID = 0;
m_FirstEventID = 0;
m_LastEventID = ~0U;
RDCEraseEl(m_ActiveQueries);
m_ActiveConditional = false;
m_ActiveFeedback = false;
if(RenderDoc::Inst().IsReplayApp())
{
m_State = READING;
if(logfile)
{
m_pSerialiser = new Serialiser(logfile, Serialiser::READING, false);
}
else
{
byte dummy[4];
m_pSerialiser = new Serialiser(4, dummy, false);
}
// once GL driver is more tested, this can be disabled
if(m_Real.glDebugMessageCallback)
{
m_Real.glDebugMessageCallback(&DebugSnoopStatic, this);
m_Real.glEnable(eGL_DEBUG_OUTPUT_SYNCHRONOUS);
}
}
else
{
m_State = WRITING_IDLE;
m_pSerialiser = new Serialiser(NULL, Serialiser::WRITING, false);
}
m_DeviceRecord = NULL;
m_ResourceManager = new GLResourceManager(m_State, m_pSerialiser, this);
m_DeviceResourceID = GetResourceManager()->RegisterResource(GLResource(NULL, eResSpecial, eSpecialResDevice));
m_ContextResourceID = GetResourceManager()->RegisterResource(GLResource(NULL, eResSpecial, eSpecialResContext));
if(!RenderDoc::Inst().IsReplayApp())
{
m_DeviceRecord = GetResourceManager()->AddResourceRecord(m_DeviceResourceID);
m_DeviceRecord->DataInSerialiser = false;
m_DeviceRecord->Length = 0;
m_DeviceRecord->SpecialResource = true;
m_ContextRecord = GetResourceManager()->AddResourceRecord(m_ContextResourceID);
m_ContextRecord->DataInSerialiser = false;
m_ContextRecord->Length = 0;
m_ContextRecord->SpecialResource = true;
// register VAO 0 as a special VAO, so that it can be tracked if the app uses it
// we immediately mark it dirty since the vertex array tracking functions expect a proper VAO
m_FakeVAOID = GetResourceManager()->RegisterResource(VertexArrayRes(NULL, 0));
GetResourceManager()->AddResourceRecord(m_FakeVAOID);
GetResourceManager()->MarkDirtyResource(m_FakeVAOID);
}
else
{
m_DeviceRecord = m_ContextRecord = NULL;
ResourceIDGen::SetReplayResourceIDs();
InitSPIRVCompiler();
RenderDoc::Inst().RegisterShutdownFunction(&ShutdownSPIRVCompiler);
}
m_FakeBB_FBO = 0;
m_FakeBB_Color = 0;
m_FakeBB_DepthStencil = 0;
m_FakeVAO = 0;
m_FakeIdxBuf = 0;
m_FakeIdxSize = 0;
RDCDEBUG("Debug Text enabled - for development! remove before release!");
m_pSerialiser->SetDebugText(true);
m_pSerialiser->SetChunkNameLookup(&GetChunkName);
//////////////////////////////////////////////////////////////////////////
// Compile time asserts
RDCCOMPILE_ASSERT(ARRAY_COUNT(GLChunkNames) == NUM_OPENGL_CHUNKS-FIRST_CHUNK_ID, "Not right number of chunk names");
}
void WrappedOpenGL::Initialise(GLInitParams &params)
{
// deliberately want to go through our own wrappers to set up e.g. m_Textures members
WrappedOpenGL &gl = *this;
m_InitParams = params;
// as a concession to compatibility, generate a 'fake' VBO to act as VBO 0.
// consider making it an error/warning for programs to use this?
gl.glGenVertexArrays(1, &m_FakeVAO);
gl.glBindVertexArray(m_FakeVAO);
gl.glBindVertexArray(0);
// we use this to draw from index data that was 'immediate' passed to the
// draw function, as i na real memory pointer
gl.glGenBuffers(1, &m_FakeIdxBuf);
gl.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, m_FakeIdxBuf);
m_FakeIdxSize = 1024*1024; // this buffer is resized up as needed
gl.glNamedBufferStorageEXT(m_FakeIdxBuf, m_FakeIdxSize, NULL, GL_DYNAMIC_STORAGE_BIT);
gl.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, 0);
gl.glGenFramebuffers(1, &m_FakeBB_FBO);
gl.glBindFramebuffer(eGL_FRAMEBUFFER, m_FakeBB_FBO);
GLenum colfmt = eGL_RGBA8;
if(params.colorBits == 32)
colfmt = params.isSRGB ? eGL_SRGB8_ALPHA8 : eGL_RGBA8;
else if(params.colorBits == 24)
colfmt = params.isSRGB ? eGL_SRGB8 : eGL_RGB8;
else
RDCERR("Unexpected # colour bits: %d", params.colorBits);
GLenum target = eGL_TEXTURE_2D;
if(params.multiSamples > 1) target = eGL_TEXTURE_2D_MULTISAMPLE;
gl.glGenTextures(1, &m_FakeBB_Color);
gl.glBindTexture(target, m_FakeBB_Color);
gl.glObjectLabel(eGL_TEXTURE, m_FakeBB_Color, -1, "Backbuffer Color");
if(params.multiSamples > 1)
{
gl.glTextureStorage2DMultisampleEXT(m_FakeBB_Color, target, params.multiSamples, colfmt, params.width, params.height, true);
}
else
{
gl.glTextureStorage2DEXT(m_FakeBB_Color, target, 1, colfmt, params.width, params.height);
gl.glTexParameteri(target, eGL_TEXTURE_MIN_FILTER, eGL_NEAREST);
gl.glTexParameteri(target, eGL_TEXTURE_MAG_FILTER, eGL_NEAREST);
gl.glTexParameteri(target, eGL_TEXTURE_WRAP_S, eGL_CLAMP_TO_EDGE);
gl.glTexParameteri(target, eGL_TEXTURE_WRAP_T, eGL_CLAMP_TO_EDGE);
}
gl.glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, m_FakeBB_Color, 0);
gl.glViewport(0, 0, params.width, params.height);
m_FakeBB_DepthStencil = 0;
if(params.depthBits > 0 || params.stencilBits > 0)
{
gl.glGenTextures(1, &m_FakeBB_DepthStencil);
gl.glBindTexture(target, m_FakeBB_DepthStencil);
GLenum depthfmt = eGL_DEPTH32F_STENCIL8;
bool stencil = false;
if(params.stencilBits == 8)
{
stencil = true;
if(params.depthBits == 32)
depthfmt = eGL_DEPTH32F_STENCIL8;
else if(params.depthBits == 24)
depthfmt = eGL_DEPTH24_STENCIL8;
else
RDCERR("Unexpected combination of depth & stencil bits: %d & %d", params.depthBits, params.stencilBits);
}
else if(params.stencilBits == 0)
{
if(params.depthBits == 32)
depthfmt = eGL_DEPTH_COMPONENT32F;
else if(params.depthBits == 24)
depthfmt = eGL_DEPTH_COMPONENT24;
else if(params.depthBits == 16)
depthfmt = eGL_DEPTH_COMPONENT16;
else
RDCERR("Unexpected # depth bits: %d", params.depthBits);
}
else
RDCERR("Unexpected # stencil bits: %d", params.stencilBits);
if(stencil)
gl.glObjectLabel(eGL_TEXTURE, m_FakeBB_DepthStencil, -1, "Backbuffer Depth-stencil");
else
gl.glObjectLabel(eGL_TEXTURE, m_FakeBB_DepthStencil, -1, "Backbuffer Depth");
if(params.multiSamples > 1)
gl.glTextureStorage2DMultisampleEXT(m_FakeBB_DepthStencil, target, params.multiSamples, depthfmt, params.width, params.height, true);
else
gl.glTextureStorage2DEXT(m_FakeBB_DepthStencil, target, 1, depthfmt, params.width, params.height);
if(stencil)
gl.glFramebufferTexture(eGL_FRAMEBUFFER, eGL_DEPTH_STENCIL_ATTACHMENT, m_FakeBB_DepthStencil, 0);
else
gl.glFramebufferTexture(eGL_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT, m_FakeBB_DepthStencil, 0);
}
}
const char *WrappedOpenGL::GetChunkName(uint32_t idx)
{
if(idx == CREATE_PARAMS) return "Create Params";
if(idx == THUMBNAIL_DATA) return "Thumbnail Data";
if(idx == DRIVER_INIT_PARAMS) return "Driver Init Params";
if(idx == INITIAL_CONTENTS) return "Initial Contents";
if(idx < FIRST_CHUNK_ID || idx >= NUM_OPENGL_CHUNKS)
return "<unknown>";
return GLChunkNames[idx-FIRST_CHUNK_ID];
}
template<>
string ToStrHelper<false, GLChunkType>::Get(const GLChunkType &el)
{
return WrappedOpenGL::GetChunkName(el);
}
WrappedOpenGL::~WrappedOpenGL()
{
if(m_FakeIdxBuf) m_Real.glDeleteBuffers(1, &m_FakeIdxBuf);
if(m_FakeVAO) m_Real.glDeleteVertexArrays(1, &m_FakeVAO);
if(m_FakeBB_FBO) m_Real.glDeleteFramebuffers(1, &m_FakeBB_FBO);
if(m_FakeBB_Color) m_Real.glDeleteTextures(1, &m_FakeBB_Color);
if(m_FakeBB_DepthStencil) m_Real.glDeleteTextures(1, &m_FakeBB_DepthStencil);
SAFE_DELETE(m_pSerialiser);
GetResourceManager()->ReleaseCurrentResource(m_DeviceResourceID);
GetResourceManager()->ReleaseCurrentResource(m_ContextResourceID);
if(m_ContextRecord)
{
RDCASSERT(m_ContextRecord->GetRefCount() == 1);
m_ContextRecord->Delete(GetResourceManager());
}
if(m_DeviceRecord)
{
RDCASSERT(m_DeviceRecord->GetRefCount() == 1);
m_DeviceRecord->Delete(GetResourceManager());
}
m_ResourceManager->Shutdown();
SAFE_DELETE(m_ResourceManager);
if(RenderDoc::Inst().GetCrashHandler())
RenderDoc::Inst().GetCrashHandler()->UnregisterMemoryRegion(this);
}
void *WrappedOpenGL::GetCtx()
{
return (void *)m_ActiveContexts[Threading::GetCurrentID()].ctx;
}
WrappedOpenGL::ContextData &WrappedOpenGL::GetCtxData()
{
return m_ContextData[GetCtx()];
}
// defined in gl_<platform>_hooks.cpp
void MakeContextCurrent(GLWindowingData data);
// for 'backwards compatible' overlay rendering
bool immediateBegin(GLenum mode, float width, float height);
void immediateVert(float x, float y, float u, float v);
void immediateEnd();
////////////////////////////////////////////////////////////////
// Windowing/setup/etc
////////////////////////////////////////////////////////////////
void WrappedOpenGL::DeleteContext(void *contextHandle)
{
ContextData &ctxdata = m_ContextData[contextHandle];
RenderDoc::Inst().RemoveDeviceFrameCapturer(ctxdata.ctx);
if(ctxdata.built && ctxdata.ready)
{
if(ctxdata.Program)
m_Real.glDeleteProgram(ctxdata.Program);
if(ctxdata.GeneralUBO)
m_Real.glDeleteBuffers(1, &ctxdata.GeneralUBO);
if(ctxdata.GlyphUBO)
m_Real.glDeleteBuffers(1, &ctxdata.GlyphUBO);
if(ctxdata.StringUBO)
m_Real.glDeleteBuffers(1, &ctxdata.StringUBO);
if(ctxdata.GlyphTexture)
m_Real.glDeleteTextures(1, &ctxdata.GlyphTexture);
}
for(auto it = m_LastContexts.begin(); it != m_LastContexts.end(); ++it)
{
if(it->ctx == contextHandle)
{
m_LastContexts.erase(it);
break;
}
}
m_ContextData.erase(contextHandle);
}
void WrappedOpenGL::ContextData::UnassociateWindow(void *wndHandle)
{
auto it = windows.find(wndHandle);
if(it != windows.end())
{
windows.erase(wndHandle);
RenderDoc::Inst().RemoveFrameCapturer(ctx, wndHandle);
}
}
void WrappedOpenGL::ContextData::AssociateWindow(WrappedOpenGL *gl, void *wndHandle)
{
auto it = windows.find(wndHandle);
if(it == windows.end())
RenderDoc::Inst().AddFrameCapturer(ctx, wndHandle, gl);
windows[wndHandle] = Timing::GetUnixTimestamp();
}
void WrappedOpenGL::ContextData::CreateDebugData(const GLHookSet &gl)
{
// to let us display the overlay on old GL contexts, use as simple a subset of functionality as possible
// to upload the texture. VAO and shaders are used optionally on modern contexts, otherwise we fall back
// to immediate mode rendering by hand
if(gl.glGetIntegerv && gl.glGenTextures && gl.glBindTexture && gl.glTexImage2D && gl.glTexParameteri)
{
string ttfstring = GetEmbeddedResource(sourcecodepro_ttf);
byte *ttfdata = (byte *)ttfstring.c_str();
byte *buf = new byte[FONT_TEX_WIDTH * FONT_TEX_HEIGHT];
stbtt_BakeFontBitmap(ttfdata, 0, charPixelHeight, buf, FONT_TEX_WIDTH, FONT_TEX_HEIGHT, firstChar, numChars, chardata);
CharSize = charPixelHeight;
CharAspect = chardata->xadvance / charPixelHeight;
stbtt_fontinfo f = {0};
stbtt_InitFont(&f, ttfdata, 0);
int ascent = 0;
stbtt_GetFontVMetrics(&f, &ascent, NULL, NULL);
float maxheight = float(ascent)*stbtt_ScaleForPixelHeight(&f, charPixelHeight);
{
GLuint curtex = 0;
gl.glGetIntegerv(eGL_TEXTURE_BINDING_2D, (GLint *)&curtex);
GLenum texFmt = eGL_R8;
if(Legacy())
texFmt = eGL_LUMINANCE;
gl.glGenTextures(1, &GlyphTexture);
gl.glBindTexture(eGL_TEXTURE_2D, GlyphTexture);
gl.glTexImage2D(eGL_TEXTURE_2D, 0, texFmt, FONT_TEX_WIDTH, FONT_TEX_HEIGHT, 0, eGL_RED, eGL_UNSIGNED_BYTE, buf);
gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAG_FILTER, eGL_LINEAR);
gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MIN_FILTER, eGL_LINEAR);
gl.glBindTexture(eGL_TEXTURE_2D, curtex);
}
delete[] buf;
Vec4f glyphData[2*(numChars+1)];
for(int i=0; i < numChars; i++)
{
stbtt_bakedchar *b = chardata+i;
float x = b->xoff;
float y = b->yoff + maxheight;
glyphData[(i+1)*2 + 0] = Vec4f(x/b->xadvance, y/charPixelHeight, b->xadvance/float(b->x1 - b->x0), charPixelHeight/float(b->y1 - b->y0));
glyphData[(i+1)*2 + 1] = Vec4f(b->x0, b->y0, b->x1, b->y1);
}
if(Modern() && gl.glGenVertexArrays && gl.glBindVertexArray)
{
GLuint curvao = 0;
gl.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&curvao);
gl.glGenVertexArrays(1, &DummyVAO);
gl.glBindVertexArray(DummyVAO);
gl.glBindVertexArray(curvao);
}
if(Modern() && gl.glGenBuffers && gl.glBufferData && gl.glBindBuffer)
{
GLuint curubo = 0;
gl.glGetIntegerv(eGL_UNIFORM_BUFFER_BINDING, (GLint *)&curubo);
gl.glGenBuffers(1, &GlyphUBO);
gl.glBindBuffer(eGL_UNIFORM_BUFFER, GlyphUBO);
gl.glBufferData(eGL_UNIFORM_BUFFER, sizeof(glyphData), glyphData, eGL_STATIC_DRAW);
gl.glGenBuffers(1, &GeneralUBO);
gl.glBindBuffer(eGL_UNIFORM_BUFFER, GeneralUBO);
gl.glBufferData(eGL_UNIFORM_BUFFER, sizeof(FontUniforms), NULL, eGL_DYNAMIC_DRAW);
gl.glGenBuffers(1, &StringUBO);
gl.glBindBuffer(eGL_UNIFORM_BUFFER, StringUBO);
gl.glBufferData(eGL_UNIFORM_BUFFER, sizeof(uint32_t)*4*FONT_MAX_CHARS, NULL, eGL_DYNAMIC_DRAW);
gl.glBindBuffer(eGL_UNIFORM_BUFFER, curubo);
}
if(Modern() &&
gl.glCreateShader && gl.glShaderSource && gl.glCompileShader && gl.glGetShaderiv && gl.glGetShaderInfoLog && gl.glDeleteShader &&
gl.glCreateProgram && gl.glAttachShader && gl.glLinkProgram && gl.glGetProgramiv && gl.glGetProgramInfoLog)
{
string textvs = "#version 420 core\n\n";
textvs += GetEmbeddedResource(glsl_debuguniforms_h);
textvs += GetEmbeddedResource(glsl_text_vert);
string textfs = GetEmbeddedResource(glsl_text_frag);
GLuint vs = gl.glCreateShader(eGL_VERTEX_SHADER);
GLuint fs = gl.glCreateShader(eGL_FRAGMENT_SHADER);
const char *src = textvs.c_str();
gl.glShaderSource(vs, 1, &src, NULL);
src = textfs.c_str();
gl.glShaderSource(fs, 1, &src, NULL);
gl.glCompileShader(vs);
gl.glCompileShader(fs);
char buffer[1024] = {0};
GLint status = 0;
gl.glGetShaderiv(vs, eGL_COMPILE_STATUS, &status);
if(status == 0)
{
gl.glGetShaderInfoLog(vs, 1024, NULL, buffer);
RDCERR("Shader error: %s", buffer);
}
gl.glGetShaderiv(fs, eGL_COMPILE_STATUS, &status);
if(status == 0)
{
gl.glGetShaderInfoLog(fs, 1024, NULL, buffer);
RDCERR("Shader error: %s", buffer);
}
Program = gl.glCreateProgram();
gl.glAttachShader(Program, vs);
gl.glAttachShader(Program, fs);
gl.glLinkProgram(Program);
gl.glGetProgramiv(Program, eGL_LINK_STATUS, &status);
if(status == 0)
{
gl.glGetProgramInfoLog(Program, 1024, NULL, buffer);
RDCERR("Link error: %s", buffer);
}
gl.glDeleteShader(vs);
gl.glDeleteShader(fs);
}
ready = true;
}
}
void WrappedOpenGL::CreateContext(GLWindowingData winData, void *shareContext, GLInitParams initParams, bool core, bool attribsCreate)
{
// TODO: support multiple GL contexts more explicitly
m_InitParams = initParams;
ContextData &ctxdata = m_ContextData[winData.ctx];
ctxdata.ctx = winData.ctx;
ctxdata.isCore = core;
ctxdata.attribsCreate = attribsCreate;
RenderDoc::Inst().AddDeviceFrameCapturer(ctxdata.ctx, this);
}
void WrappedOpenGL::RegisterContext(GLWindowingData winData, void *shareContext, bool core, bool attribsCreate)
{
ContextData &ctxdata = m_ContextData[winData.ctx];
ctxdata.ctx = winData.ctx;
ctxdata.isCore = core;
ctxdata.attribsCreate = attribsCreate;
}
void WrappedOpenGL::ActivateContext(GLWindowingData winData)
{
m_ActiveContexts[Threading::GetCurrentID()] = winData;
if(winData.ctx)
{
for(auto it = m_LastContexts.begin(); it != m_LastContexts.end(); ++it)
{
if(it->ctx == winData.ctx)
{
m_LastContexts.erase(it);
break;
}
}
m_LastContexts.push_back(winData);
if(m_LastContexts.size() > 10)
m_LastContexts.erase(m_LastContexts.begin());
}
// TODO: support multiple GL contexts more explicitly
Keyboard::AddInputWindow((void *)winData.wnd);
if(winData.ctx)
{
// if we're capturing, we need to serialise out the changed state vector
if(m_State == WRITING_CAPFRAME)
{
// fetch any initial states needed. Note this is insufficient, and doesn't handle the case where
// we might just suddenly start getting commands on a thread that already has a context active.
// For now we assume we'll only get GL commands from a single thread
QueuedInitialStateFetch fetch;
fetch.res.Context = winData.ctx;
auto it = std::lower_bound(m_QueuedInitialFetches.begin(), m_QueuedInitialFetches.end(), fetch);
for(; it != m_QueuedInitialFetches.end(); )
{
GetResourceManager()->Prepare_InitialState(it->res, it->blob);
it = m_QueuedInitialFetches.erase(it);
}
SCOPED_SERIALISE_CONTEXT(CONTEXT_CAPTURE_HEADER);
Serialise_BeginCaptureFrame(false);
m_ContextRecord->AddChunk(scope.Get());
}
ContextData &ctxdata = m_ContextData[winData.ctx];
if(!ctxdata.built)
{
ctxdata.built = true;
const GLHookSet &gl = m_Real;
if(gl.glDebugMessageCallback && RenderDoc::Inst().GetCaptureOptions().DebugDeviceMode)
{
gl.glDebugMessageCallback(&DebugSnoopStatic, this);
gl.glEnable(eGL_DEBUG_OUTPUT_SYNCHRONOUS);
}
vector<string> implExts;
if(gl.glGetIntegerv && gl.glGetStringi)
{
GLuint numExts = 0;
gl.glGetIntegerv(eGL_NUM_EXTENSIONS, (GLint *)&numExts);
for(GLuint i=0; i < numExts; i++)
implExts.push_back((const char *)gl.glGetStringi(eGL_EXTENSIONS, i));
}
else if(gl.glGetString)
{
string implExtString = (const char *)gl.glGetString(eGL_EXTENSIONS);
split(implExtString, implExts, ' ');
}
else
{
RDCERR("No functions to fetch implementation's extensions!");
}
std::sort(implExts.begin(), implExts.end());
// intersection of implExts and globalExts into ctx.glExts
{
for(size_t i=0, j=0; i < implExts.size() && j < globalExts.size(); )
{
string &a = implExts[i];
string &b = globalExts[j];
if(a == b)
{
ctxdata.glExts.push_back(a);
i++;
j++;
}
else if(a < b)
{
i++;
}
else if(b < a)
{
j++;
}
}
}
// this extension is something RenderDoc will support even if the impl
// doesn't. https://renderdoc.org/debug_tool.txt
ctxdata.glExts.push_back("GL_EXT_debug_tool");
merge(ctxdata.glExts, ctxdata.glExtsString, ' ');
if(gl.glGetIntegerv)
{
GLint mj = 0, mn = 0;
gl.glGetIntegerv(eGL_MAJOR_VERSION, &mj);
gl.glGetIntegerv(eGL_MINOR_VERSION, &mn);
int ver = mj*10 + mn;
ctxdata.version = ver;
if(ver > GLCoreVersion || (!GLIsCore && ctxdata.isCore))
{
GLCoreVersion = ver;
GLIsCore = ctxdata.isCore;
DoVendorChecks(gl, winData);
}
}
}
}
}
void WrappedOpenGL::WindowSize(void *windowHandle, uint32_t w, uint32_t h)
{
// TODO: support multiple window handles
m_InitParams.width = w;
m_InitParams.height = h;
}
// TODO this could be a general class for use elsewhere (ie. code that wants
// to push and pop would set state through the class, which records dirty bits
// and then restores).
struct RenderTextState
{
bool enableBits[8];
GLenum ClipOrigin, ClipDepth;
GLenum EquationRGB, EquationAlpha;
GLenum SourceRGB, SourceAlpha;
GLenum DestinationRGB, DestinationAlpha;
GLenum PolygonMode;
GLfloat Viewportf[4];
GLint Viewport[4];
GLenum ActiveTexture;
GLuint tex0;
GLuint ubo[3];
GLuint prog;
GLuint pipe;
GLuint VAO;
GLuint drawFBO;
// if this context wasn't created with CreateContextAttribs we
// do an immediate mode render, so fewer states are pushed/popped.
// note we don't assume a 1.0 context since that would be painful to
// handle. Instead we just skip bits of state we're not going to mess
// with. In some cases this might cause problems e.g. we don't use
// indexed enable states for blend and scissor test because we're
// assuming there's no separate blending.
//
// In the end, this is just a best-effort to keep going without
// crashing. Old GL versions aren't supported.
void Push(const GLHookSet &gl, bool modern)
{
enableBits[0] = gl.glIsEnabled(eGL_DEPTH_TEST) != 0;
enableBits[1] = gl.glIsEnabled(eGL_STENCIL_TEST) != 0;
enableBits[2] = gl.glIsEnabled(eGL_CULL_FACE) != 0;
if(modern)
{
enableBits[3] = gl.glIsEnabled(eGL_DEPTH_CLAMP) != 0;
enableBits[4] = gl.glIsEnabledi(eGL_BLEND, 0) != 0;
enableBits[5] = gl.glIsEnabledi(eGL_SCISSOR_TEST, 0) != 0;
}
else
{
enableBits[3] = gl.glIsEnabled(eGL_BLEND) != 0;
enableBits[4] = gl.glIsEnabled(eGL_SCISSOR_TEST) != 0;
enableBits[5] = gl.glIsEnabled(eGL_TEXTURE_2D) != 0;
enableBits[6] = gl.glIsEnabled(eGL_LIGHTING) != 0;
enableBits[7] = gl.glIsEnabled(eGL_ALPHA_TEST) != 0;
}
if(modern && (GLCoreVersion >= 45 || ExtensionSupported[ExtensionSupported_ARB_clip_control]))
{
gl.glGetIntegerv(eGL_CLIP_ORIGIN, (GLint *)&ClipOrigin);
gl.glGetIntegerv(eGL_CLIP_DEPTH_MODE, (GLint *)&ClipDepth);
}
else
{
ClipOrigin = eGL_LOWER_LEFT;
ClipDepth = eGL_NEGATIVE_ONE_TO_ONE;
}
if(modern)
{
gl.glGetIntegeri_v(eGL_BLEND_EQUATION_RGB, 0, (GLint*)&EquationRGB);
gl.glGetIntegeri_v(eGL_BLEND_EQUATION_ALPHA, 0, (GLint*)&EquationAlpha);
gl.glGetIntegeri_v(eGL_BLEND_SRC_RGB, 0, (GLint*)&SourceRGB);
gl.glGetIntegeri_v(eGL_BLEND_SRC_ALPHA, 0, (GLint*)&SourceAlpha);
gl.glGetIntegeri_v(eGL_BLEND_DST_RGB, 0, (GLint*)&DestinationRGB);
gl.glGetIntegeri_v(eGL_BLEND_DST_ALPHA, 0, (GLint*)&DestinationAlpha);
}
else
{
gl.glGetIntegerv(eGL_BLEND_EQUATION_RGB, (GLint*)&EquationRGB);
gl.glGetIntegerv(eGL_BLEND_EQUATION_ALPHA, (GLint*)&EquationAlpha);
gl.glGetIntegerv(eGL_BLEND_SRC_RGB, (GLint*)&SourceRGB);
gl.glGetIntegerv(eGL_BLEND_SRC_ALPHA, (GLint*)&SourceAlpha);
gl.glGetIntegerv(eGL_BLEND_DST_RGB, (GLint*)&DestinationRGB);
gl.glGetIntegerv(eGL_BLEND_DST_ALPHA, (GLint*)&DestinationAlpha);
}
if(!VendorCheck[VendorCheck_AMD_polygon_mode_query])
{
GLenum dummy[2] = { eGL_FILL, eGL_FILL };
// docs suggest this is enumeration[2] even though polygon mode can't be set independently for front
// and back faces.
gl.glGetIntegerv(eGL_POLYGON_MODE, (GLint *)&dummy);
PolygonMode = dummy[0];
}
else
{
PolygonMode = eGL_FILL;
}
if(modern)
gl.glGetFloati_v(eGL_VIEWPORT, 0, &Viewportf[0]);
else
gl.glGetIntegerv(eGL_VIEWPORT, &Viewport[0]);
gl.glGetIntegerv(eGL_ACTIVE_TEXTURE, (GLint *)&ActiveTexture);
gl.glActiveTexture(eGL_TEXTURE0);
gl.glGetIntegerv(eGL_TEXTURE_BINDING_2D, (GLint*)&tex0);
// we get the current program but only try to restore it if it's non-0
prog = 0;
gl.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint *)&prog);
drawFBO = 0;
gl.glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, (GLint *)&drawFBO);
// since we will use the fixed function pipeline, also need to check for
// program pipeline bindings (if we weren't, our program would override)
pipe = 0;
gl.glGetIntegerv(eGL_PROGRAM_PIPELINE_BINDING, (GLint *)&pipe);
if(modern)
{
gl.glGetIntegeri_v(eGL_UNIFORM_BUFFER_BINDING, 0, (GLint*)&ubo[0]);
gl.glGetIntegeri_v(eGL_UNIFORM_BUFFER_BINDING, 1, (GLint*)&ubo[1]);
gl.glGetIntegeri_v(eGL_UNIFORM_BUFFER_BINDING, 2, (GLint*)&ubo[2]);
gl.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&VAO);
}
}
void Pop(const GLHookSet &gl, bool modern)
{
if(enableBits[0]) gl.glEnable(eGL_DEPTH_TEST); else gl.glDisable(eGL_DEPTH_TEST);
if(enableBits[1]) gl.glEnable(eGL_STENCIL_TEST); else gl.glDisable(eGL_STENCIL_TEST);
if(enableBits[2]) gl.glEnable(eGL_CULL_FACE); else gl.glDisable(eGL_CULL_FACE);
if(modern)
{
if(enableBits[3]) gl.glEnable(eGL_DEPTH_CLAMP); else gl.glDisable(eGL_DEPTH_CLAMP);
if(enableBits[4]) gl.glEnablei(eGL_BLEND, 0); else gl.glDisablei(eGL_BLEND, 0);
if(enableBits[5]) gl.glEnablei(eGL_SCISSOR_TEST, 0); else gl.glDisablei(eGL_SCISSOR_TEST, 0);
}
else
{
if(enableBits[3]) gl.glEnable(eGL_BLEND); else gl.glDisable(eGL_BLEND);
if(enableBits[4]) gl.glEnable(eGL_SCISSOR_TEST); else gl.glDisable(eGL_SCISSOR_TEST);
if(enableBits[5]) gl.glEnable(eGL_TEXTURE_2D); else gl.glDisable(eGL_TEXTURE_2D);
if(enableBits[6]) gl.glEnable(eGL_LIGHTING); else gl.glDisable(eGL_LIGHTING);
if(enableBits[7]) gl.glEnable(eGL_ALPHA_TEST); else gl.glDisable(eGL_ALPHA_TEST);
}
if(modern && gl.glClipControl && (GLCoreVersion >= 45 || ExtensionSupported[ExtensionSupported_ARB_clip_control])) // only available in 4.5+
gl.glClipControl(ClipOrigin, ClipDepth);
if(modern)
{
gl.glBlendFuncSeparatei(0, SourceRGB, DestinationRGB, SourceAlpha, DestinationAlpha);
gl.glBlendEquationSeparatei(0, EquationRGB, EquationAlpha);
}
else
{
gl.glBlendFuncSeparate(SourceRGB, DestinationRGB, SourceAlpha, DestinationAlpha);
gl.glBlendEquationSeparate(EquationRGB, EquationAlpha);
}
gl.glPolygonMode(eGL_FRONT_AND_BACK, PolygonMode);
if(modern)
gl.glViewportIndexedf(0, Viewportf[0], Viewportf[1], Viewportf[2], Viewportf[3]);
else
gl.glViewport(Viewport[0], Viewport[1], (GLsizei)Viewport[2], (GLsizei)Viewport[3]);
gl.glActiveTexture(eGL_TEXTURE0);
gl.glBindTexture(eGL_TEXTURE_2D, tex0);
gl.glActiveTexture(ActiveTexture);
if(drawFBO != 0 && gl.glBindFramebuffer)
gl.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, drawFBO);
if(modern)
{
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, ubo[0]);
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 1, ubo[1]);
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 2, ubo[2]);
gl.glUseProgram(prog);
gl.glBindVertexArray(VAO);
}
else
{
// only restore these if there was a setting and the function pointer exists
if(gl.glUseProgram && prog != 0)
gl.glUseProgram(prog);
if(gl.glBindProgramPipeline && pipe != 0)
gl.glBindProgramPipeline(pipe);
}
}
};
void WrappedOpenGL::RenderOverlayText(float x, float y, const char *fmt, ...)
{
static char tmpBuf[4096];
va_list args;
va_start(args, fmt);
StringFormat::vsnprintf( tmpBuf, 4095, fmt, args );
tmpBuf[4095] = '\0';
va_end(args);
RenderOverlayStr(x, y, tmpBuf);
}
void WrappedOpenGL::RenderOverlayStr(float x, float y, const char *text)
{
if(char *t = strchr((char *)text, '\n'))
{
*t = 0;
RenderOverlayStr(x, y, text);
RenderOverlayStr(x, y+1.0f, t+1);
*t = '\n';
return;
}
if(strlen(text) == 0)
return;
const GLHookSet &gl = m_Real;
RDCASSERT(strlen(text) < (size_t)FONT_MAX_CHARS);
ContextData &ctxdata = m_ContextData[GetCtx()];
if(!ctxdata.built || !ctxdata.ready) return;
// if it's reasonably modern context, assume we can use buffers and UBOs
if(ctxdata.Modern())
{
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, ctxdata.GeneralUBO);
FontUniforms *ubo = (FontUniforms *)gl.glMapBufferRange(eGL_UNIFORM_BUFFER, 0, sizeof(FontUniforms), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
ubo->TextPosition.x = x;
ubo->TextPosition.y = y;
ubo->FontScreenAspect.x = 1.0f/float(m_InitParams.width);
ubo->FontScreenAspect.y = 1.0f/float(m_InitParams.height);
ubo->TextSize = ctxdata.CharSize;
ubo->FontScreenAspect.x *= ctxdata.CharAspect;
ubo->CharacterSize.x = 1.0f/float(FONT_TEX_WIDTH);
ubo->CharacterSize.y = 1.0f/float(FONT_TEX_HEIGHT);
gl.glUnmapBuffer(eGL_UNIFORM_BUFFER);
size_t len = strlen(text);
if((int)len > FONT_MAX_CHARS)
{
static bool printedWarning = false;
// this could be called once a frame, don't want to spam the log
if(!printedWarning)
{
printedWarning = true;
RDCWARN("log string '%s' is too long", text, (int)len);
}
len = FONT_MAX_CHARS;
}
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, ctxdata.StringUBO);
uint32_t *texs = (uint32_t *)gl.glMapBufferRange(eGL_UNIFORM_BUFFER, 0, len*4*sizeof(uint32_t), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
if(texs)
{
for(size_t i=0; i < len; i++)
{
texs[i*4+0] = text[i] - ' ';
texs[i*4+1] = text[i] - ' ';
texs[i*4+2] = text[i] - ' ';
texs[i*4+3] = text[i] - ' ';
}
}
else
{
static bool printedWarning = false;
// this could be called once a frame, don't want to spam the log
if(!printedWarning)
{
printedWarning = true;
RDCWARN("failed to map %d characters for '%s' (%d)", (int)len, text, ctxdata.StringUBO);
}
}
gl.glUnmapBuffer(eGL_UNIFORM_BUFFER);
//////////////////////////////////////////////////////////////////////////////////
// Make sure if you change any other state in here, that you also update the push
// and pop functions above (RenderTextState)
// set blend state
gl.glEnablei(eGL_BLEND, 0);
gl.glBlendFuncSeparatei(0, eGL_SRC_ALPHA, eGL_ONE_MINUS_SRC_ALPHA, eGL_SRC_ALPHA, eGL_SRC_ALPHA);
gl.glBlendEquationSeparatei(0, eGL_FUNC_ADD, eGL_FUNC_ADD);
// set depth & stencil
gl.glDisable(eGL_DEPTH_TEST);
gl.glDisable(eGL_DEPTH_CLAMP);
gl.glDisable(eGL_STENCIL_TEST);
gl.glDisable(eGL_CULL_FACE);
gl.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, 0);
// set viewport & scissor
gl.glViewportIndexedf(0, 0.0f, 0.0f, (float)m_InitParams.width, (float)m_InitParams.height);
gl.glDisablei(eGL_SCISSOR_TEST, 0);
gl.glPolygonMode(eGL_FRONT_AND_BACK, eGL_FILL);
if(gl.glClipControl && (GLCoreVersion >= 45 || ExtensionSupported[ExtensionSupported_ARB_clip_control])) // only available in 4.5+
gl.glClipControl(eGL_LOWER_LEFT, eGL_NEGATIVE_ONE_TO_ONE);
// bind UBOs
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, ctxdata.GeneralUBO);
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 1, ctxdata.GlyphUBO);
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 2, ctxdata.StringUBO);
// bind empty VAO just for valid rendering
gl.glBindVertexArray(ctxdata.DummyVAO);
// bind textures
gl.glActiveTexture(eGL_TEXTURE0);
gl.glBindTexture(eGL_TEXTURE_2D, ctxdata.GlyphTexture);
// bind program
gl.glUseProgram(ctxdata.Program);
// draw string
gl.glDrawArraysInstanced(eGL_TRIANGLE_STRIP, 0, 4, (GLsizei)len);
}
else
{
// if it wasn't created in modern fashion with createattribs, assume the worst
// and draw with immediate mode (since it's impossible that the context is core
// profile, this will always work)
//
// This isn't perfect since without a lot of fiddling we'd need to check if e.g.
// indexed blending should be used or not. Since we're not too worried about
// working in this situation, just doing something reasonable, we just assume
// roughly ~2.0 functionality
//////////////////////////////////////////////////////////////////////////////////
// Make sure if you change any other state in here, that you also update the push
// and pop functions above (RenderTextState)
// disable blending and some old-style fixed function features
gl.glDisable(eGL_BLEND);
gl.glDisable(eGL_LIGHTING);
gl.glDisable(eGL_ALPHA_TEST);
// set depth & stencil
gl.glDisable(eGL_DEPTH_TEST);
gl.glDisable(eGL_STENCIL_TEST);
gl.glDisable(eGL_CULL_FACE);
// set viewport & scissor
gl.glViewport(0, 0, (GLsizei)m_InitParams.width, (GLsizei)m_InitParams.height);
gl.glDisable(eGL_SCISSOR_TEST);
gl.glPolygonMode(eGL_FRONT_AND_BACK, eGL_FILL);
// bind textures
gl.glActiveTexture(eGL_TEXTURE0);
gl.glBindTexture(eGL_TEXTURE_2D, ctxdata.GlyphTexture);
gl.glEnable(eGL_TEXTURE_2D);
gl.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, 0);
// just in case, try to disable the programmable pipeline
if(gl.glUseProgram)
gl.glUseProgram(0);
if(gl.glBindProgramPipeline)
gl.glBindProgramPipeline(0);
// draw string (based on sample code from stb_truetype.h)
if(immediateBegin(eGL_QUADS, (float)m_InitParams.width, (float)m_InitParams.height))
{
y += 1.0f;
y *= charPixelHeight;
float startx = x;
float starty = y;
float maxx = x, minx = x;
float maxy = y, miny = y - charPixelHeight;
stbtt_aligned_quad q;
const char *prepass = text;
while (*prepass)
{
char c = *prepass;
if (c >= firstChar && c <= lastChar)
{
stbtt_GetBakedQuad(chardata, FONT_TEX_WIDTH, FONT_TEX_HEIGHT, c-firstChar, &x, &y, &q, 1);
maxx = RDCMAX(maxx, RDCMAX(q.x0, q.x1));
maxy = RDCMAX(maxy, RDCMAX(q.y0, q.y1));
minx = RDCMIN(minx, RDCMIN(q.x0, q.x1));
miny = RDCMIN(miny, RDCMIN(q.y0, q.y1));
}
else
{
x += chardata[0].xadvance;
}
prepass++;
}
x = startx;
y = starty;
// draw black bar behind text
immediateVert(minx, maxy, 0.0f, 0.0f);
immediateVert(maxx, maxy, 0.0f, 0.0f);
immediateVert(maxx, miny, 0.0f, 0.0f);
immediateVert(minx, miny, 0.0f, 0.0f);
while (*text)
{
char c = *text;
if (c >= firstChar && c <= lastChar)
{
stbtt_GetBakedQuad(chardata, FONT_TEX_WIDTH, FONT_TEX_HEIGHT, c-firstChar, &x, &y, &q, 1);
immediateVert(q.x0, q.y0, q.s0, q.t0);
immediateVert(q.x1, q.y0, q.s1, q.t0);
immediateVert(q.x1, q.y1, q.s1, q.t1);
immediateVert(q.x0, q.y1, q.s0, q.t1);
maxx = RDCMAX(maxx, RDCMAX(q.x0, q.x1));
maxy = RDCMAX(maxy, RDCMAX(q.y0, q.y1));
}
else
{
x += chardata[0].xadvance;
}
++text;
}
immediateEnd();
}
}
}
struct ReplacementSearch
{
bool operator()(const pair<ResourceId, Replacement> &a, ResourceId b)
{
return a.first < b;
}
};
void WrappedOpenGL::ReplaceResource(ResourceId from, ResourceId to)
{
RemoveReplacement(from);
if(GetResourceManager()->HasLiveResource(from))
{
GLResource resource = GetResourceManager()->GetLiveResource(to);
ResourceId livefrom = GetResourceManager()->GetLiveID(from);
if(resource.Namespace == eResShader)
{
// need to replace all programs that use this shader
for(auto it=m_Programs.begin(); it != m_Programs.end(); ++it)
{
ResourceId progsrcid = it->first;
ProgramData &progdata = it->second;
// see if the shader is used
for(int i=0; i < 6; i++)
{
if(progdata.stageShaders[i] == livefrom)
{
GLuint progsrc = GetResourceManager()->GetCurrentResource(progsrcid).name;
// make a new program
GLuint progdst = glCreateProgram();
ResourceId progdstid = GetResourceManager()->GetID(ProgramRes(GetCtx(), progdst));
// attach all but the i'th shader
for(int j=0; j < 6; j++)
if(i != j && progdata.stageShaders[j] != ResourceId())
glAttachShader(progdst, GetResourceManager()->GetCurrentResource(progdata.stageShaders[j]).name);
// attach the new shader
glAttachShader(progdst, resource.name);
// mark separable if previous program was separable
GLint sep = 0;
glGetProgramiv(progsrc, eGL_PROGRAM_SEPARABLE, &sep);
if(sep)
glProgramParameteri(progdst, eGL_PROGRAM_SEPARABLE, GL_TRUE);
ResourceId vs = progdata.stageShaders[0];
ResourceId fs = progdata.stageShaders[4];
if(vs != ResourceId())
CopyProgramAttribBindings(m_Real, progsrc, progdst, &m_Shaders[vs].reflection);
if(fs != ResourceId())
CopyProgramFragDataBindings(m_Real, progsrc, progdst, &m_Shaders[fs].reflection);
// link new program
glLinkProgram(progdst);
GLint status = 0;
glGetProgramiv(progdst, eGL_LINK_STATUS, &status);
if(status == 0)
{
GLint len = 1024;
glGetProgramiv(progdst, eGL_INFO_LOG_LENGTH, &len);
char *buffer = new char[len+1];
glGetProgramInfoLog(progdst, len, NULL, buffer); buffer[len] = 0;
RDCWARN("When making program replacement for shader, program failed to link. Skipping replacement:\n%s", buffer);
delete[] buffer;
glDeleteProgram(progdst);
}
else
{
// copy uniforms
CopyProgramUniforms(m_Real, progsrc, progdst);
ResourceId origsrcid = GetResourceManager()->GetOriginalID(progsrcid);
// recursively call to replaceresource (different type - these are programs)
ReplaceResource(origsrcid, progdstid);
// insert into m_DependentReplacements
auto insertPos = std::lower_bound(m_DependentReplacements.begin(), m_DependentReplacements.end(), from, ReplacementSearch());
m_DependentReplacements.insert(insertPos, std::make_pair(from, Replacement(origsrcid, ProgramRes(GetCtx(), progdst))));
}
break;
}
}
}
}
if(resource.Namespace == eResProgram)
{
// need to replace all pipelines that use this program
for(auto it=m_Pipelines.begin(); it != m_Pipelines.end(); ++it)
{
ResourceId pipesrcid = it->first;
PipelineData &pipedata = it->second;
// see if the program is used
for(int i=0; i < 6; i++)
{
if(pipedata.stagePrograms[i] == livefrom)
{
// make a new pipeline
GLuint pipedst = 0;
glGenProgramPipelines(1, &pipedst);
ResourceId pipedstid = GetResourceManager()->GetID(ProgramPipeRes(GetCtx(), pipedst));
// attach all but the i'th program
for(int j=0; j < 6; j++)
{
if(i != j && pipedata.stagePrograms[j] != ResourceId())
{
// if this stage was provided by the program we're replacing, use that instead
if(pipedata.stagePrograms[i] == pipedata.stagePrograms[j])
glUseProgramStages(pipedst, ShaderBit(j), resource.name);
else
glUseProgramStages(pipedst, ShaderBit(j), GetResourceManager()->GetCurrentResource(pipedata.stagePrograms[j]).name);
}
}
// attach the new program in our stage
glUseProgramStages(pipedst, ShaderBit(i), resource.name);
ResourceId origsrcid = GetResourceManager()->GetOriginalID(pipesrcid);
// recursively call to replaceresource (different type - these are programs)
ReplaceResource(origsrcid, pipedstid);
// insert into m_DependentReplacements
auto insertPos = std::lower_bound(m_DependentReplacements.begin(), m_DependentReplacements.end(), from, ReplacementSearch());
m_DependentReplacements.insert(insertPos, std::make_pair(from, Replacement(origsrcid, ProgramPipeRes(GetCtx(), pipedst))));
}
}
}
}
// do actual replacement
GLResource fromresource = GetResourceManager()->GetLiveResource(from);
// if they're the same type it's easy, but it could be we want to replace a shader
// inside a program which never had a shader (ie. glCreateShaderProgramv)
if(fromresource.Namespace == resource.Namespace)
{
GetResourceManager()->ReplaceResource(from, to);
}
else if(fromresource.Namespace == eResProgram && resource.Namespace == eResShader)
{
// if we want to replace a program with a shader, assume it's just a program with only one
// shader attached. This will have been handled above in the "programs dependent on this
// shader", so we can just skip doing anything here
}
else
{
RDCERR("Unsupported replacement type from type %d to type %d", fromresource.Namespace, resource.Namespace);
}
}
}
void WrappedOpenGL::RemoveReplacement(ResourceId id)
{
// do actual removal
GetResourceManager()->RemoveReplacement(id);
std::set<ResourceId> recurse;
// check if there are any dependent replacements, remove if so
auto it = std::lower_bound(m_DependentReplacements.begin(), m_DependentReplacements.end(), id, ReplacementSearch());
for(; it != m_DependentReplacements.end(); )
{
GetResourceManager()->RemoveReplacement(it->second.id);
recurse.insert(it->second.id);
switch(it->second.res.Namespace)
{
case eResProgram:
glDeleteProgram(it->second.res.name);
break;
case eResProgramPipe:
glDeleteProgramPipelines(1, &it->second.res.name);
break;
default:
RDCERR("Unexpected resource type to be freed");
break;
}
it = m_DependentReplacements.erase(it);
}
for(auto recurseit = recurse.begin(); recurseit != recurse.end(); ++recurseit)
{
// recursive call in case there are any dependents on this resource
RemoveReplacement(*recurseit);
}
}
void WrappedOpenGL::FreeTargetResource(ResourceId id)
{
if(GetResourceManager()->HasLiveResource(id))
{
GLResource resource = GetResourceManager()->GetLiveResource(id);
RDCASSERT(resource.Namespace != eResUnknown);
switch(resource.Namespace)
{
case eResShader:
glDeleteShader(resource.name);
break;
default:
RDCERR("Unexpected resource type to be freed");
break;
}
}
}
void WrappedOpenGL::SwapBuffers(void *windowHandle)
{
if(m_State == WRITING_IDLE)
RenderDoc::Inst().Tick();
// don't do anything if no context is active.
if(GetCtx() == NULL)
return;
m_FrameCounter++; // first present becomes frame #1, this function is at the end of the frame
GetResourceManager()->FlushPendingDirty();
ContextData &ctxdata = GetCtxData();
// we only handle context-window associations here as it's too common to
// create invisible helper windows while creating contexts, that then
// become the default window.
// Since we only capture windows that do SwapBuffers (i.e. if you're doing
// headless rendering then you must capture via the API anyway), this
// isn't a big problem.
//
// Also we only set up associations for capturable windows.
if(ctxdata.Modern())
{
for(auto it=m_ContextData.begin(); it != m_ContextData.end(); ++it)
if(it->first != ctxdata.ctx)
it->second.UnassociateWindow(windowHandle);
ctxdata.AssociateWindow(this, windowHandle);
}
// do this as late as possible to avoid creating objects on contexts
// that might be shared later (wglShareLists requires contexts to be
// pristine, so can't create this from wglMakeCurrent)
if(!ctxdata.ready)
ctxdata.CreateDebugData(m_Real);
bool activeWindow = RenderDoc::Inst().IsActiveWindow(ctxdata.ctx, windowHandle);
// look at previous associations and decay any that are too old
uint64_t ref = Timing::GetUnixTimestamp() - 5; // 5 seconds
for(auto cit=m_ContextData.begin(); cit != m_ContextData.end(); ++cit)
{
for(auto wit=cit->second.windows.begin(); wit != cit->second.windows.end(); )
{
if(wit->second < ref)
{
auto remove = wit;
++wit;
cit->second.windows.erase(remove);
}
else
{
++wit;
}
}
}
if(m_State == WRITING_IDLE)
{
m_FrameTimes.push_back(m_FrameTimer.GetMilliseconds());
m_TotalTime += m_FrameTimes.back();
m_FrameTimer.Restart();
// update every second
if(m_TotalTime > 1000.0)
{
m_MinFrametime = 10000.0;
m_MaxFrametime = 0.0;
m_AvgFrametime = 0.0;
m_TotalTime = 0.0;
for(size_t i=0; i < m_FrameTimes.size(); i++)
{
m_AvgFrametime += m_FrameTimes[i];
if(m_FrameTimes[i] < m_MinFrametime)
m_MinFrametime = m_FrameTimes[i];
if(m_FrameTimes[i] > m_MaxFrametime)
m_MaxFrametime = m_FrameTimes[i];
}
m_AvgFrametime /= double(m_FrameTimes.size());
m_FrameTimes.clear();
}
uint32_t overlay = RenderDoc::Inst().GetOverlayBits();
if(overlay & eRENDERDOC_Overlay_Enabled)
{
RenderTextState textState;
textState.Push(m_Real, ctxdata.Modern());
if(activeWindow)
{
vector<RENDERDOC_InputButton> keys = RenderDoc::Inst().GetCaptureKeys();
string overlayText = "OpenGL.";
if(ctxdata.Modern())
{
if(Keyboard::PlatformHasKeyInput())
{
overlayText += " ";
for(size_t i=0; i < keys.size(); i++)
{
if(i > 0)
overlayText += ", ";
overlayText += ToStr::Get(keys[i]);
}
if(!keys.empty())
overlayText += " to capture.";
}
else
{
if(RenderDoc::Inst().IsRemoteAccessConnected())
overlayText += "Connected by " + RenderDoc::Inst().GetRemoteAccessUsername() + ".";
else
overlayText += "No remote access connection.";
}
}
if(overlay & eRENDERDOC_Overlay_FrameNumber)
{
overlayText += StringFormat::Fmt(" Frame: %d.", m_FrameCounter);
}
if(overlay & eRENDERDOC_Overlay_FrameRate)
{
overlayText += StringFormat::Fmt(" %.2lf ms (%.2lf .. %.2lf) (%.0lf FPS)",
m_AvgFrametime, m_MinFrametime, m_MaxFrametime,
m_AvgFrametime <= 0.0f ? 0.0f : 1000.0f/m_AvgFrametime);
}
float y=0.0f;
if(!overlayText.empty())
{
RenderOverlayText(0.0f, y, overlayText.c_str());
y += 1.0f;
}
if(ctxdata.Legacy())
{
if(!ctxdata.attribsCreate)
{
RenderOverlayText(0.0f, y, "Context not created via CreateContextAttribs. Capturing disabled.");
y += 1.0f;
}
RenderOverlayText(0.0f, y, "Only OpenGL 3.2+ contexts are supported.");
y += 1.0f;
}
else if(!ctxdata.isCore)
{
RenderOverlayText(0.0f, y, "WARNING: Non-core context in use. Compatibility profile not supported.");
y += 1.0f;
}
if(ctxdata.Modern() && (overlay & eRENDERDOC_Overlay_CaptureList))
{
RenderOverlayText(0.0f, y, "%d Captures saved.\n", (uint32_t)m_CapturedFrames.size());
y += 1.0f;
uint64_t now = Timing::GetUnixTimestamp();
for(size_t i=0; i < m_CapturedFrames.size(); i++)
{
if(now - m_CapturedFrames[i].captureTime < 20)
{
RenderOverlayText(0.0f, y, "Captured frame %d.\n", m_CapturedFrames[i].frameNumber);
y += 1.0f;
}
}
}
if(m_FailedFrame > 0)
{
const char *reasonString = "Unknown reason";
switch(m_FailedReason)
{
case CaptureFailed_UncappedUnmap: reasonString = "Uncapped Map()/Unmap()"; break;
default: break;
}
RenderOverlayText(0.0f, y, "Failed capture at frame %d:\n", m_FailedFrame);
y += 1.0f;
RenderOverlayText(0.0f, y, " %s\n", reasonString);
y += 1.0f;
}
#if !defined(RELEASE)
RenderOverlayText(0.0f, y, "%llu chunks - %.2f MB", Chunk::NumLiveChunks(), float(Chunk::TotalMem())/1024.0f/1024.0f);
y += 1.0f;
#endif
}
else
{
vector<RENDERDOC_InputButton> keys = RenderDoc::Inst().GetFocusKeys();
string str = "OpenGL. Inactive window.";
if(ctxdata.Modern())
{
for(size_t i=0; i < keys.size(); i++)
{
if(i == 0)
str += " ";
else
str += ", ";
str += ToStr::Get(keys[i]);
}
if(!keys.empty())
str += " to cycle between windows.";
}
else
{
if(!ctxdata.attribsCreate)
{
str += "\nContext not created via CreateContextAttribs. Capturing disabled.\n";
}
str += "Only OpenGL 3.2+ contexts are supported.";
}
RenderOverlayText(0.0f, 0.0f, str.c_str());
}
textState.Pop(m_Real, ctxdata.Modern());
// swallow all errors we might have inadvertantly caused. This is
// better than letting an error propagate and maybe screw up the
// app (although it means we might swallow an error from before the
// SwapBuffers call, it can't be helped.
if(ctxdata.Legacy() && m_Real.glGetError)
ClearGLErrors(m_Real);
}
}
if(m_State == WRITING_CAPFRAME && m_AppControlledCapture)
m_BackbufferImages[windowHandle] = SaveBackbufferImage();
if(!activeWindow)
return;
RenderDoc::Inst().SetCurrentDriver(RDC_OpenGL);
// only allow capturing on 'modern' created contexts
if(ctxdata.Legacy())
return;
// kill any current capture that isn't application defined
if(m_State == WRITING_CAPFRAME && !m_AppControlledCapture)
RenderDoc::Inst().EndFrameCapture(ctxdata.ctx, windowHandle);
if(RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter) && m_State == WRITING_IDLE)
{
RenderDoc::Inst().StartFrameCapture(ctxdata.ctx, windowHandle);
m_AppControlledCapture = false;
}
}
void WrappedOpenGL::MakeValidContextCurrent(GLWindowingData &prevctx, void *favourWnd)
{
if(prevctx.ctx == NULL)
{
for(size_t i=m_LastContexts.size(); i > 0; i--)
{
// need to find a context for fetching most initial states
GLWindowingData ctx = m_LastContexts[i-1];
// check this context isn't current elsewhere
bool usedElsewhere = false;
for(auto it=m_ActiveContexts.begin(); it != m_ActiveContexts.end(); ++it)
{
if(it->second.ctx == ctx.ctx)
{
usedElsewhere = true;
break;
}
}
if(!usedElsewhere)
{
prevctx = ctx;
break;
}
}
if(prevctx.ctx == NULL)
{
RDCERR("Couldn't find GL context to make current on this thread %llu.", Threading::GetCurrentID());
}
m_ActiveContexts[Threading::GetCurrentID()] = prevctx;
MakeContextCurrent(prevctx);
}
}
void WrappedOpenGL::StartFrameCapture(void *dev, void *wnd)
{
if(m_State != WRITING_IDLE) return;
RenderDoc::Inst().SetCurrentDriver(RDC_OpenGL);
m_State = WRITING_CAPFRAME;
m_AppControlledCapture = true;
m_Failures = 0;
m_FailedFrame = 0;
m_FailedReason = CaptureSucceeded;
GLWindowingData prevctx = m_ActiveContexts[Threading::GetCurrentID()];
GLWindowingData switchctx = prevctx;
MakeValidContextCurrent(switchctx, wnd);
m_FrameCounter = RDCMAX(1+(uint32_t)m_CapturedFrames.size(), m_FrameCounter);
FetchFrameInfo frame;
frame.frameNumber = m_FrameCounter+1;
frame.captureTime = Timing::GetUnixTimestamp();
RDCEraseEl(frame.stats);
m_CapturedFrames.push_back(frame);
GetResourceManager()->ClearReferencedResources();
GetResourceManager()->MarkResourceFrameReferenced(m_DeviceResourceID, eFrameRef_Write);
GetResourceManager()->PrepareInitialContents();
FreeCaptureData();
AttemptCapture();
BeginCaptureFrame();
if(switchctx.ctx != prevctx.ctx)
{
MakeContextCurrent(prevctx);
m_ActiveContexts[Threading::GetCurrentID()] = prevctx;
}
RDCLOG("Starting capture, frame %u", m_FrameCounter);
}
bool WrappedOpenGL::EndFrameCapture(void *dev, void *wnd)
{
if(m_State != WRITING_CAPFRAME) return true;
CaptureFailReason reason = CaptureSucceeded;
GLWindowingData prevctx = m_ActiveContexts[Threading::GetCurrentID()];
GLWindowingData switchctx = prevctx;
MakeValidContextCurrent(switchctx, wnd);
if(HasSuccessfulCapture(reason))
{
RDCLOG("Finished capture, Frame %u", m_FrameCounter);
m_Failures = 0;
m_FailedFrame = 0;
m_FailedReason = CaptureSucceeded;
ContextEndFrame();
FinishCapture();
BackbufferImage *bbim = NULL;
// if the specified context isn't current, try and see if we've saved
// an appropriate backbuffer image during capture.
if( (dev != NULL && prevctx.ctx != dev) || (wnd != 0 && (void *)prevctx.wnd != wnd) )
{
auto it = m_BackbufferImages.find(wnd);
if(it != m_BackbufferImages.end())
{
// pop this backbuffer image out of the map
bbim = it->second;
m_BackbufferImages.erase(it);
}
}
// if we don't have one selected, save the backbuffer image from the
// current context
if(bbim == NULL)
bbim = SaveBackbufferImage();
Serialiser *m_pFileSerialiser = RenderDoc::Inst().OpenWriteSerialiser(m_FrameCounter, &m_InitParams, bbim->jpgbuf, bbim->len, bbim->thwidth, bbim->thheight);
SAFE_DELETE(bbim);
for(auto it=m_BackbufferImages.begin(); it != m_BackbufferImages.end(); ++it)
delete it->second;
m_BackbufferImages.clear();
{
SCOPED_SERIALISE_CONTEXT(DEVICE_INIT);
SERIALISE_ELEMENT(ResourceId, immContextId, m_ContextResourceID);
SERIALISE_ELEMENT(ResourceId, vaoId, m_FakeVAOID);
m_pFileSerialiser->Insert(scope.Get(true));
}
RDCDEBUG("Inserting Resource Serialisers");
GetResourceManager()->InsertReferencedChunks(m_pFileSerialiser);
GetResourceManager()->InsertInitialContentsChunks(m_pFileSerialiser);
RDCDEBUG("Creating Capture Scope");
{
SCOPED_SERIALISE_CONTEXT(CAPTURE_SCOPE);
Serialise_CaptureScope(0);
m_pFileSerialiser->Insert(scope.Get(true));
}
{
RDCDEBUG("Getting Resource Record");
GLResourceRecord *record = m_ResourceManager->GetResourceRecord(m_ContextResourceID);
RDCDEBUG("Accumulating context resource list");
map<int32_t, Chunk *> recordlist;
record->Insert(recordlist);
RDCDEBUG("Flushing %u records to file serialiser", (uint32_t)recordlist.size());
for(auto it = recordlist.begin(); it != recordlist.end(); ++it)
m_pFileSerialiser->Insert(it->second);
RDCDEBUG("Done");
}
m_pFileSerialiser->FlushToDisk();
RenderDoc::Inst().SuccessfullyWrittenLog();
SAFE_DELETE(m_pFileSerialiser);
m_State = WRITING_IDLE;
GetResourceManager()->MarkUnwrittenResources();
GetResourceManager()->ClearReferencedResources();
if(switchctx.ctx != prevctx.ctx)
{
MakeContextCurrent(prevctx);
m_ActiveContexts[Threading::GetCurrentID()] = prevctx;
}
return true;
}
else
{
const char *reasonString = "Unknown reason";
switch(reason)
{
case CaptureFailed_UncappedUnmap: reasonString = "Uncapped Map()/Unmap()"; break;
default: break;
}
RDCLOG("Failed to capture, frame %u: %s", m_FrameCounter, reasonString);
m_Failures++;
if((RenderDoc::Inst().GetOverlayBits() & eRENDERDOC_Overlay_Enabled))
{
ContextData &ctxdata = GetCtxData();
RenderTextState textState;
textState.Push(m_Real, ctxdata.Modern());
RenderOverlayText(0.0f, 0.0f, "Failed to capture frame %u: %s", m_FrameCounter, reasonString);
textState.Pop(m_Real, ctxdata.Modern());
// swallow all errors we might have inadvertantly caused. This is
// better than letting an error propagate and maybe screw up the
// app (although it means we might swallow an error from before the
// SwapBuffers call, it can't be helped.
if(ctxdata.Legacy() && m_Real.glGetError)
ClearGLErrors(m_Real);
}
m_CapturedFrames.back().frameNumber = m_FrameCounter+1;
CleanupCapture();
GetResourceManager()->ClearReferencedResources();
// if it's a capture triggered from application code, immediately
// give up as it's not reasonable to expect applications to detect and retry.
// otherwise we can retry in case the next frame works.
if(m_Failures > 5 || m_AppControlledCapture)
{
FinishCapture();
m_CapturedFrames.pop_back();
FreeCaptureData();
m_FailedFrame = m_FrameCounter;
m_FailedReason = reason;
m_State = WRITING_IDLE;
GetResourceManager()->MarkUnwrittenResources();
}
else
{
GetResourceManager()->MarkResourceFrameReferenced(m_DeviceResourceID, eFrameRef_Write);
GetResourceManager()->PrepareInitialContents();
AttemptCapture();
BeginCaptureFrame();
}
if(switchctx.ctx != prevctx.ctx)
{
MakeContextCurrent(prevctx);
m_ActiveContexts[Threading::GetCurrentID()] = prevctx;
}
return false;
}
}
WrappedOpenGL::BackbufferImage *WrappedOpenGL::SaveBackbufferImage()
{
byte *thpixels = NULL;
uint32_t thwidth = 0;
uint32_t thheight = 0;
if(m_Real.glGetIntegerv && m_Real.glReadBuffer && m_Real.glBindFramebuffer && m_Real.glBindBuffer && m_Real.glReadPixels)
{
RDCGLenum prevReadBuf = eGL_BACK;
GLint prevBuf = 0;
GLint packBufBind = 0;
GLint prevPackRowLen = 0;
GLint prevPackSkipRows = 0;
GLint prevPackSkipPixels = 0;
GLint prevPackAlignment = 0;
m_Real.glGetIntegerv(eGL_READ_BUFFER, (GLint *)&prevReadBuf);
m_Real.glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &prevBuf);
m_Real.glGetIntegerv(eGL_PIXEL_PACK_BUFFER_BINDING, &packBufBind);
m_Real.glGetIntegerv(eGL_PACK_ROW_LENGTH, &prevPackRowLen);
m_Real.glGetIntegerv(eGL_PACK_SKIP_ROWS, &prevPackSkipRows);
m_Real.glGetIntegerv(eGL_PACK_SKIP_PIXELS, &prevPackSkipPixels);
m_Real.glGetIntegerv(eGL_PACK_ALIGNMENT, &prevPackAlignment);
m_Real.glBindFramebuffer(eGL_READ_FRAMEBUFFER, 0);
m_Real.glReadBuffer(eGL_BACK);
m_Real.glBindBuffer(eGL_PIXEL_PACK_BUFFER, 0);
m_Real.glPixelStorei(eGL_PACK_ROW_LENGTH, 0);
m_Real.glPixelStorei(eGL_PACK_SKIP_ROWS, 0);
m_Real.glPixelStorei(eGL_PACK_SKIP_PIXELS, 0);
m_Real.glPixelStorei(eGL_PACK_ALIGNMENT, 1);
thwidth = m_InitParams.width;
thheight = m_InitParams.height;
thpixels = new byte[thwidth*thheight*3];
m_Real.glReadPixels(0, 0, thwidth, thheight, eGL_RGB, eGL_UNSIGNED_BYTE, thpixels);
for(uint32_t y=0; y <= thheight/2; y++)
{
for(uint32_t x=0; x < thwidth; x++)
{
byte save[3];
save[0] = thpixels[y*(thwidth*3) + x*3 + 0];
save[1] = thpixels[y*(thwidth*3) + x*3 + 1];
save[2] = thpixels[y*(thwidth*3) + x*3 + 2];
thpixels[y*(thwidth*3) + x*3 + 0] = thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 0];
thpixels[y*(thwidth*3) + x*3 + 1] = thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 1];
thpixels[y*(thwidth*3) + x*3 + 2] = thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 2];
thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 0] = save[0];
thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 1] = save[1];
thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 2] = save[2];
}
}
m_Real.glBindBuffer(eGL_PIXEL_PACK_BUFFER, packBufBind);
m_Real.glBindFramebuffer(eGL_READ_FRAMEBUFFER, prevBuf);
m_Real.glReadBuffer(prevReadBuf);
m_Real.glPixelStorei(eGL_PACK_ROW_LENGTH, prevPackRowLen);
m_Real.glPixelStorei(eGL_PACK_SKIP_ROWS, prevPackSkipRows);
m_Real.glPixelStorei(eGL_PACK_SKIP_PIXELS, prevPackSkipPixels);
m_Real.glPixelStorei(eGL_PACK_ALIGNMENT, prevPackAlignment);
}
byte *jpgbuf = NULL;
int len = thwidth*thheight;
if(len > 0)
{
jpgbuf = new byte[len];
jpge::params p;
p.m_quality = 40;
bool success = jpge::compress_image_to_jpeg_file_in_memory(jpgbuf, len, thwidth, thheight, 3, thpixels, p);
if(!success)
{
RDCERR("Failed to compress to jpg");
SAFE_DELETE_ARRAY(jpgbuf);
thwidth = 0;
thheight = 0;
}
}
BackbufferImage *bbim = new BackbufferImage();
bbim->jpgbuf = jpgbuf;
bbim->len = len;
bbim->thwidth = thwidth;
bbim->thheight = thheight;
return bbim;
}
void WrappedOpenGL::Serialise_CaptureScope(uint64_t offset)
{
SERIALISE_ELEMENT(uint32_t, FrameNumber, m_FrameCounter);
if(m_State >= WRITING)
{
GetResourceManager()->Serialise_InitialContentsNeeded();
}
else
{
m_FrameRecord.frameInfo.fileOffset = offset;
m_FrameRecord.frameInfo.firstEvent = 1;//m_pImmediateContext->GetEventID();
m_FrameRecord.frameInfo.frameNumber = FrameNumber;
m_FrameRecord.frameInfo.immContextId = GetResourceManager()->GetOriginalID(m_ContextResourceID);
RDCEraseEl(m_FrameRecord.frameInfo.stats);
GetResourceManager()->CreateInitialContents();
}
}
void WrappedOpenGL::ContextEndFrame()
{
SCOPED_SERIALISE_CONTEXT(CONTEXT_CAPTURE_FOOTER);
bool HasCallstack = RenderDoc::Inst().GetCaptureOptions().CaptureCallstacks != 0;
m_pSerialiser->Serialise("HasCallstack", HasCallstack);
if(HasCallstack)
{
Callstack::Stackwalk *call = Callstack::Collect();
RDCASSERT(call->NumLevels() < 0xff);
size_t numLevels = call->NumLevels();
uint64_t *stack = (uint64_t *)call->GetAddrs();
m_pSerialiser->SerialisePODArray("callstack", stack, numLevels);
delete call;
}
m_ContextRecord->AddChunk(scope.Get());
}
void WrappedOpenGL::CleanupCapture()
{
m_SuccessfulCapture = true;
m_FailureReason = CaptureSucceeded;
m_ContextRecord->LockChunks();
while(m_ContextRecord->HasChunks())
{
Chunk *chunk = m_ContextRecord->GetLastChunk();
SAFE_DELETE(chunk);
m_ContextRecord->PopChunk();
}
m_ContextRecord->UnlockChunks();
m_ContextRecord->FreeParents(GetResourceManager());
for(auto it=m_MissingTracks.begin(); it != m_MissingTracks.end(); ++it)
{
if(GetResourceManager()->HasResourceRecord(*it))
GetResourceManager()->MarkDirtyResource(*it);
}
m_MissingTracks.clear();
}
void WrappedOpenGL::FreeCaptureData()
{
}
void WrappedOpenGL::QueuePrepareInitialState(GLResource res, byte *blob)
{
QueuedInitialStateFetch fetch;
fetch.res = res;
fetch.blob = blob;
auto insertPos = std::lower_bound(m_QueuedInitialFetches.begin(), m_QueuedInitialFetches.end(), fetch);
m_QueuedInitialFetches.insert(insertPos, fetch);
}
void WrappedOpenGL::AttemptCapture()
{
m_State = WRITING_CAPFRAME;
m_DebugMessages.clear();
{
RDCDEBUG("GL Context %llu Attempting capture", GetContextResourceID());
m_SuccessfulCapture = true;
m_FailureReason = CaptureSucceeded;
m_ContextRecord->LockChunks();
while(m_ContextRecord->HasChunks())
{
Chunk *chunk = m_ContextRecord->GetLastChunk();
SAFE_DELETE(chunk);
m_ContextRecord->PopChunk();
}
m_ContextRecord->UnlockChunks();
}
}
bool WrappedOpenGL::Serialise_BeginCaptureFrame(bool applyInitialState)
{
GLRenderState state(&m_Real, m_pSerialiser, m_State);
if(m_State >= WRITING)
{
state.FetchState(GetCtx(), this);
state.MarkReferenced(this, true);
}
state.Serialise(m_State, GetCtx(), this);
if(m_State <= EXECUTING && applyInitialState)
{
m_DoStateVerify = false;
state.ApplyState(GetCtx(), this);
m_DoStateVerify = true;
}
return true;
}
void WrappedOpenGL::BeginCaptureFrame()
{
SCOPED_SERIALISE_CONTEXT(CONTEXT_CAPTURE_HEADER);
Serialise_BeginCaptureFrame(false);
m_ContextRecord->AddChunk(scope.Get(), 1);
}
void WrappedOpenGL::FinishCapture()
{
m_State = WRITING_IDLE;
m_DebugMessages.clear();
//m_SuccessfulCapture = false;
}
void WrappedOpenGL::AddDebugMessage(DebugMessageCategory c, DebugMessageSeverity sv, DebugMessageSource src, std::string d)
{
if(m_State == READING || src == eDbgSource_RuntimeWarning)
{
DebugMessage msg;
msg.eventID = m_CurEventID;
msg.messageID = 0;
msg.source = src;
msg.category = c;
msg.severity = sv;
msg.description = d;
m_DebugMessages.push_back(msg);
}
}
vector<DebugMessage> WrappedOpenGL::GetDebugMessages()
{
vector<DebugMessage> ret;
ret.swap(m_DebugMessages);
return ret;
}
void WrappedOpenGL::Serialise_DebugMessages()
{
SCOPED_SERIALISE_CONTEXT(DEBUG_MESSAGES);
vector<DebugMessage> debugMessages;
if(m_State == WRITING_CAPFRAME)
{
debugMessages = m_DebugMessages;
m_DebugMessages.clear();
}
SERIALISE_ELEMENT(bool, HasCallstack, RenderDoc::Inst().GetCaptureOptions().CaptureCallstacksOnlyDraws != 0);
if(HasCallstack)
{
if(m_State >= WRITING)
{
Callstack::Stackwalk *call = Callstack::Collect();
RDCASSERT(call->NumLevels() < 0xff);
size_t numLevels = call->NumLevels();
uint64_t *stack = (uint64_t *)call->GetAddrs();
m_pSerialiser->SerialisePODArray("callstack", stack, numLevels);
delete call;
}
else
{
size_t numLevels = 0;
uint64_t *stack = NULL;
m_pSerialiser->SerialisePODArray("callstack", stack, numLevels);
m_pSerialiser->SetCallstack(stack, numLevels);
SAFE_DELETE_ARRAY(stack);
}
}
SERIALISE_ELEMENT(uint32_t, NumMessages, (uint32_t)debugMessages.size());
for(uint32_t i=0; i < NumMessages; i++)
{
ScopedContext msgscope(m_pSerialiser, "DebugMessage", "DebugMessage", 0, false);
string desc;
if(m_State >= WRITING)
desc = debugMessages[i].description.elems;
SERIALISE_ELEMENT(uint32_t, Category, debugMessages[i].category);
SERIALISE_ELEMENT(uint32_t, Severity, debugMessages[i].severity);
SERIALISE_ELEMENT(uint32_t, ID, debugMessages[i].messageID);
SERIALISE_ELEMENT(string, Description, desc);
if(m_State == READING)
{
DebugMessage msg;
msg.eventID = m_CurEventID;
msg.source = eDbgSource_API;
msg.category = (DebugMessageCategory)Category;
msg.severity = (DebugMessageSeverity)Severity;
msg.messageID = ID;
msg.description = Description;
m_DebugMessages.push_back(msg);
}
}
}
bool WrappedOpenGL::RecordUpdateCheck(GLResourceRecord *record)
{
// if nothing is bound, don't serialise chunk
if(record == NULL) return false;
// if we've already stopped tracking this object, return as such
if(record && record->UpdateCount > 64)
return false;
// increase update count
record->UpdateCount++;
// if update count is high, mark as dirty
if(record && record->UpdateCount > 64)
{
GetResourceManager()->MarkDirtyResource( record->GetResourceID() );
return false;
}
return true;
}
void WrappedOpenGL::DebugSnoop(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message)
{
if(type != eGL_DEBUG_TYPE_PUSH_GROUP && type != eGL_DEBUG_TYPE_POP_GROUP)
{
if(type != eGL_DEBUG_TYPE_PERFORMANCE && type != eGL_DEBUG_TYPE_OTHER)
{
RDCLOG("Got a Debug message from %s, type %s, ID %d, severity %s:\n'%s'",
ToStr::Get(source).c_str(), ToStr::Get(type).c_str(), id, ToStr::Get(severity).c_str(), message);
if(m_DebugMsgContext != "")
RDCLOG("Debug Message context: \"%s\"", m_DebugMsgContext.c_str());
}
if(m_State == WRITING_CAPFRAME)
{
DebugMessage msg;
msg.messageID = id;
msg.description = string(message, message+length);
switch(severity)
{
case eGL_DEBUG_SEVERITY_HIGH:
msg.severity = eDbgSeverity_High; break;
case eGL_DEBUG_SEVERITY_MEDIUM:
msg.severity = eDbgSeverity_Medium; break;
case eGL_DEBUG_SEVERITY_LOW:
msg.severity = eDbgSeverity_Low; break;
case eGL_DEBUG_SEVERITY_NOTIFICATION:
default:
msg.severity = eDbgSeverity_Info; break;
}
if(source == eGL_DEBUG_SOURCE_APPLICATION || type == eGL_DEBUG_TYPE_MARKER)
{
msg.category = eDbgCategory_Application_Defined;
}
else if(source == eGL_DEBUG_SOURCE_SHADER_COMPILER)
{
msg.category = eDbgCategory_Shaders;
}
else
{
switch(type)
{
case eGL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
msg.category = eDbgCategory_Deprecated; break;
case eGL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
msg.category = eDbgCategory_Undefined; break;
case eGL_DEBUG_TYPE_PORTABILITY:
msg.category = eDbgCategory_Portability; break;
case eGL_DEBUG_TYPE_PERFORMANCE:
msg.category = eDbgCategory_Performance; break;
case eGL_DEBUG_TYPE_ERROR:
case eGL_DEBUG_TYPE_OTHER:
default:
msg.category = eDbgCategory_Miscellaneous; break;
}
}
m_DebugMessages.push_back(msg);
}
}
if(m_RealDebugFunc)
m_RealDebugFunc(source, type, id, severity, length, message, m_RealDebugFuncParam);
}
void WrappedOpenGL::ReadLogInitialisation()
{
uint64_t frameOffset = 0;
m_pSerialiser->SetDebugText(true);
m_pSerialiser->Rewind();
int chunkIdx = 0;
struct chunkinfo
{
chunkinfo() : count(0), totalsize(0), total(0.0) {}
int count;
uint64_t totalsize;
double total;
};
map<GLChunkType,chunkinfo> chunkInfos;
SCOPED_TIMER("chunk initialisation");
for(;;)
{
PerformanceTimer timer;
uint64_t offset = m_pSerialiser->GetOffset();
GLChunkType context = (GLChunkType)m_pSerialiser->PushContext(NULL, NULL, 1, false);
if(context == CAPTURE_SCOPE)
{
// immediately read rest of log into memory
m_pSerialiser->SetPersistentBlock(offset);
}
chunkIdx++;
ProcessChunk(offset, context);
m_pSerialiser->PopContext(context);
RenderDoc::Inst().SetProgress(FileInitialRead, float(offset)/float(m_pSerialiser->GetSize()));
if(context == CAPTURE_SCOPE)
{
frameOffset = offset;
GetResourceManager()->ApplyInitialContents();
ContextReplayLog(READING, 0, 0, false);
}
uint64_t offset2 = m_pSerialiser->GetOffset();
chunkInfos[context].total += timer.GetMilliseconds();
chunkInfos[context].totalsize += offset2 - offset;
chunkInfos[context].count++;
if(context == CAPTURE_SCOPE)
break;
if(m_pSerialiser->AtEnd())
break;
}
#if !defined(RELEASE)
for(auto it=chunkInfos.begin(); it != chunkInfos.end(); ++it)
{
double dcount = double(it->second.count);
RDCDEBUG("% 5d chunks - Time: %9.3fms total/%9.3fms avg - Size: %8.3fMB total/%7.3fMB avg - %s (%u)",
it->second.count,
it->second.total, it->second.total/dcount,
double(it->second.totalsize)/(1024.0*1024.0),
double(it->second.totalsize)/(dcount*1024.0*1024.0),
GetChunkName(it->first), uint32_t(it->first)
);
}
#endif
m_FrameRecord.frameInfo.fileSize = m_pSerialiser->GetSize();
m_FrameRecord.frameInfo.persistentSize = m_pSerialiser->GetSize() - frameOffset;
m_FrameRecord.frameInfo.initDataSize = chunkInfos[(GLChunkType)INITIAL_CONTENTS].totalsize;
RDCDEBUG("Allocating %llu persistant bytes of memory for the log.", m_pSerialiser->GetSize() - frameOffset);
m_pSerialiser->SetDebugText(false);
}
void WrappedOpenGL::ProcessChunk(uint64_t offset, GLChunkType context)
{
switch(context)
{
case DEVICE_INIT:
{
SERIALISE_ELEMENT(ResourceId, immContextId, ResourceId());
SERIALISE_ELEMENT(ResourceId, vaoId, ResourceId());
GetResourceManager()->AddLiveResource(immContextId, GLResource(NULL, eResSpecial, eSpecialResContext));
GetResourceManager()->AddLiveResource(vaoId, VertexArrayRes(NULL, 0));
break;
}
case GEN_TEXTURE:
Serialise_glGenTextures(0, NULL);
break;
case CREATE_TEXTURE:
Serialise_glCreateTextures(eGL_NONE, 0, NULL);
break;
case ACTIVE_TEXTURE:
Serialise_glActiveTexture(eGL_NONE);
break;
case BIND_TEXTURE:
Serialise_glBindTexture(eGL_NONE, 0);
break;
case BIND_TEXTURES:
Serialise_glBindTextures(0, 0, NULL);
break;
case BIND_MULTI_TEX:
Serialise_glBindMultiTextureEXT(eGL_NONE, eGL_NONE, 0);
break;
case BIND_TEXTURE_UNIT:
Serialise_glBindTextureUnit(0, 0);
break;
case BIND_IMAGE_TEXTURE:
Serialise_glBindImageTexture(0, 0, 0, 0, 0, eGL_NONE, eGL_NONE);
break;
case BIND_IMAGE_TEXTURES:
Serialise_glBindImageTextures(0, 0, NULL);
break;
case TEXSTORAGE1D:
Serialise_glTextureStorage1DEXT(0, eGL_NONE, 0, eGL_NONE, 0);
break;
case TEXSTORAGE2D:
Serialise_glTextureStorage2DEXT(0, eGL_NONE, 0, eGL_NONE, 0, 0);
break;
case TEXSTORAGE3D:
Serialise_glTextureStorage3DEXT(0, eGL_NONE, 0, eGL_NONE, 0, 0, 0);
break;
case TEXSTORAGE2DMS:
Serialise_glTextureStorage2DMultisampleEXT(0, eGL_NONE, 0, eGL_NONE, 0, 0, GL_FALSE);
break;
case TEXSTORAGE3DMS:
Serialise_glTextureStorage3DMultisampleEXT(0, eGL_NONE, 0, eGL_NONE, 0, 0, 0, GL_FALSE);
break;
case TEXIMAGE1D:
Serialise_glTextureImage1DEXT(0, eGL_NONE, 0, 0, 0, 0, eGL_NONE, eGL_NONE, NULL);
break;
case TEXIMAGE2D:
Serialise_glTextureImage2DEXT(0, eGL_NONE, 0, 0, 0, 0, 0, eGL_NONE, eGL_NONE, NULL);
break;
case TEXIMAGE3D:
Serialise_glTextureImage3DEXT(0, eGL_NONE, 0, 0, 0, 0, 0, 0, eGL_NONE, eGL_NONE, NULL);
break;
case TEXSUBIMAGE1D:
Serialise_glTextureSubImage1DEXT(0, eGL_NONE, 0, 0, 0, eGL_NONE, eGL_NONE, NULL);
break;
case TEXSUBIMAGE2D:
Serialise_glTextureSubImage2DEXT(0, eGL_NONE, 0, 0, 0, 0, 0, eGL_NONE, eGL_NONE, NULL);
break;
case TEXSUBIMAGE3D:
Serialise_glTextureSubImage3DEXT(0, eGL_NONE, 0, 0, 0, 0, 0, 0, 0, eGL_NONE, eGL_NONE, NULL);
break;
case TEXIMAGE1D_COMPRESSED:
Serialise_glCompressedTextureImage1DEXT(0, eGL_NONE, 0, eGL_NONE, 0, 0, 0, NULL);
break;
case TEXIMAGE2D_COMPRESSED:
Serialise_glCompressedTextureImage2DEXT(0, eGL_NONE, 0, eGL_NONE, 0, 0, 0, 0, NULL);
break;
case TEXIMAGE3D_COMPRESSED:
Serialise_glCompressedTextureImage3DEXT(0, eGL_NONE, 0, eGL_NONE, 0, 0, 0, 0, 0, NULL);
break;
case TEXSUBIMAGE1D_COMPRESSED:
Serialise_glCompressedTextureSubImage1DEXT(0, eGL_NONE, 0, 0, 0, eGL_NONE, 0, NULL);
break;
case TEXSUBIMAGE2D_COMPRESSED:
Serialise_glCompressedTextureSubImage2DEXT(0, eGL_NONE, 0, 0, 0, 0, 0, eGL_NONE, 0, NULL);
break;
case TEXSUBIMAGE3D_COMPRESSED:
Serialise_glCompressedTextureSubImage3DEXT(0, eGL_NONE, 0, 0, 0, 0, 0, 0, 0, eGL_NONE, 0, NULL);
break;
case TEXBUFFER:
Serialise_glTextureBufferEXT(0, eGL_NONE, eGL_NONE, 0);
break;
case TEXBUFFER_RANGE:
Serialise_glTextureBufferRangeEXT(0, eGL_NONE, eGL_NONE, 0, 0, 0);
break;
case PIXELSTORE:
Serialise_glPixelStorei(eGL_NONE, 0);
break;
case TEXPARAMETERF:
Serialise_glTextureParameterfEXT(0, eGL_NONE, eGL_NONE, 0);
break;
case TEXPARAMETERFV:
Serialise_glTextureParameterfvEXT(0, eGL_NONE, eGL_NONE, NULL);
break;
case TEXPARAMETERI:
Serialise_glTextureParameteriEXT(0, eGL_NONE, eGL_NONE, 0);
break;
case TEXPARAMETERIV:
Serialise_glTextureParameterivEXT(0, eGL_NONE, eGL_NONE, NULL);
break;
case TEXPARAMETERIIV:
Serialise_glTextureParameterIivEXT(0, eGL_NONE, eGL_NONE, NULL);
break;
case TEXPARAMETERIUIV:
Serialise_glTextureParameterIuivEXT(0, eGL_NONE, eGL_NONE, NULL);
break;
case GENERATE_MIPMAP:
Serialise_glGenerateTextureMipmapEXT(0, eGL_NONE);
break;
case COPY_SUBIMAGE:
Serialise_glCopyImageSubData(0, eGL_NONE, 0, 0, 0, 0, 0, eGL_NONE, 0, 0, 0, 0, 0, 0, 0);
break;
case COPY_IMAGE1D:
Serialise_glCopyTextureImage1DEXT(0, eGL_NONE, 0, eGL_NONE, 0, 0, 0, 0);
break;
case COPY_IMAGE2D:
Serialise_glCopyTextureImage2DEXT(0, eGL_NONE, 0, eGL_NONE, 0, 0, 0, 0, 0);
break;
case COPY_SUBIMAGE1D:
Serialise_glCopyTextureSubImage1DEXT(0, eGL_NONE, 0, 0, 0, 0, 0);
break;
case COPY_SUBIMAGE2D:
Serialise_glCopyTextureSubImage2DEXT(0, eGL_NONE, 0, 0, 0, 0, 0, 0, 0);
break;
case COPY_SUBIMAGE3D:
Serialise_glCopyTextureSubImage3DEXT(0, eGL_NONE, 0, 0, 0, 0, 0, 0, 0, 0);
break;
case TEXTURE_VIEW:
Serialise_glTextureView(0, eGL_NONE, 0, eGL_NONE, 0, 0, 0, 0);
break;
case CREATE_SHADER:
Serialise_glCreateShader(0, eGL_NONE);
break;
case CREATE_PROGRAM:
Serialise_glCreateProgram(0);
break;
case CREATE_SHADERPROGRAM:
Serialise_glCreateShaderProgramv(0, eGL_NONE, 0, NULL);
break;
case COMPILESHADER:
Serialise_glCompileShader(0);
break;
case SHADERSOURCE:
Serialise_glShaderSource(0, 0, NULL, NULL);
break;
case ATTACHSHADER:
Serialise_glAttachShader(0, 0);
break;
case DETACHSHADER:
Serialise_glDetachShader(0, 0);
break;
case USEPROGRAM:
Serialise_glUseProgram(0);
break;
case PROGRAMPARAMETER:
Serialise_glProgramParameteri(0, eGL_NONE, 0);
break;
case FEEDBACK_VARYINGS:
Serialise_glTransformFeedbackVaryings(0, 0, NULL, eGL_NONE);
break;
case BINDATTRIB_LOCATION:
Serialise_glBindAttribLocation(0, 0, NULL);
break;
case BINDFRAGDATA_LOCATION:
Serialise_glBindFragDataLocation(0, 0, NULL);
break;
case BINDFRAGDATA_LOCATION_INDEXED:
Serialise_glBindFragDataLocationIndexed(0, 0, 0, NULL);
break;
case UNIFORM_BLOCKBIND:
Serialise_glUniformBlockBinding(0, 0, 0);
break;
case STORAGE_BLOCKBIND:
Serialise_glShaderStorageBlockBinding(0, 0, 0);
break;
case UNIFORM_SUBROUTINE:
Serialise_glUniformSubroutinesuiv(eGL_NONE, 0, NULL);
break;
case PROGRAMUNIFORM_VECTOR:
Serialise_glProgramUniformVector(0, eGL_NONE, 0, 0, UNIFORM_UNKNOWN);
break;
case PROGRAMUNIFORM_MATRIX:
Serialise_glProgramUniformMatrix(0, 0, 0, 0, NULL, UNIFORM_UNKNOWN);
break;
case LINKPROGRAM:
Serialise_glLinkProgram(0);
break;
case NAMEDSTRING:
Serialise_glNamedStringARB(eGL_NONE, 0, NULL, 0, NULL);
break;
case DELETENAMEDSTRING:
Serialise_glDeleteNamedStringARB(0, NULL);
break;
case COMPILESHADERINCLUDE:
Serialise_glCompileShaderIncludeARB(0, 0, NULL, NULL);
break;
case GEN_FEEDBACK:
Serialise_glGenTransformFeedbacks(0, NULL);
break;
case CREATE_FEEDBACK:
Serialise_glCreateTransformFeedbacks(0, NULL);
break;
case BIND_FEEDBACK:
Serialise_glBindTransformFeedback(eGL_NONE, 0);
break;
case BEGIN_FEEDBACK:
Serialise_glBeginTransformFeedback(eGL_NONE);
break;
case END_FEEDBACK:
Serialise_glEndTransformFeedback();
break;
case PAUSE_FEEDBACK:
Serialise_glPauseTransformFeedback();
break;
case RESUME_FEEDBACK:
Serialise_glResumeTransformFeedback();
break;
case GEN_PROGRAMPIPE:
Serialise_glGenProgramPipelines(0, NULL);
break;
case CREATE_PROGRAMPIPE:
Serialise_glCreateProgramPipelines(0, NULL);
break;
case USE_PROGRAMSTAGES:
Serialise_glUseProgramStages(0, 0, 0);
break;
case BIND_PROGRAMPIPE:
Serialise_glBindProgramPipeline(0);
break;
case FENCE_SYNC:
Serialise_glFenceSync(NULL, eGL_NONE, 0);
break;
case CLIENTWAIT_SYNC:
Serialise_glClientWaitSync(NULL, 0, 0);
break;
case WAIT_SYNC:
Serialise_glWaitSync(NULL, 0, 0);
break;
case GEN_QUERIES:
Serialise_glGenQueries(0, NULL);
break;
case CREATE_QUERIES:
Serialise_glCreateQueries(eGL_NONE, 0, NULL);
break;
case BEGIN_QUERY:
Serialise_glBeginQuery(eGL_NONE, 0);
break;
case BEGIN_QUERY_INDEXED:
Serialise_glBeginQueryIndexed(eGL_NONE, 0, 0);
break;
case END_QUERY:
Serialise_glEndQuery(eGL_NONE);
break;
case END_QUERY_INDEXED:
Serialise_glEndQueryIndexed(eGL_NONE, 0);
break;
case BEGIN_CONDITIONAL:
Serialise_glBeginConditionalRender(0, eGL_NONE);
break;
case END_CONDITIONAL:
Serialise_glEndConditionalRender();
break;
case QUERY_COUNTER:
Serialise_glQueryCounter(0, eGL_NONE);
break;
case CLEAR_COLOR:
Serialise_glClearColor(0, 0, 0, 0);
break;
case CLEAR_DEPTH:
Serialise_glClearDepth(0);
break;
case CLEAR_STENCIL:
Serialise_glClearStencil(0);
break;
case CLEAR:
Serialise_glClear(0);
break;
case CLEARBUFFERF:
Serialise_glClearNamedFramebufferfv(0, eGL_NONE, 0, NULL);
break;
case CLEARBUFFERI:
Serialise_glClearNamedFramebufferiv(0, eGL_NONE, 0, NULL);
break;
case CLEARBUFFERUI:
Serialise_glClearNamedFramebufferuiv(0, eGL_NONE, 0, NULL);
break;
case CLEARBUFFERFI:
Serialise_glClearNamedFramebufferfi(0, eGL_NONE, 0, 0);
break;
case CLEARBUFFERDATA:
Serialise_glClearNamedBufferDataEXT(0, eGL_NONE, eGL_NONE, eGL_NONE, NULL);
break;
case CLEARBUFFERSUBDATA:
Serialise_glClearNamedBufferSubDataEXT(0, eGL_NONE, 0, 0, eGL_NONE, eGL_NONE, NULL);
break;
case CLEARTEXIMAGE:
Serialise_glClearTexImage(0, 0, eGL_NONE, eGL_NONE, NULL);
break;
case CLEARTEXSUBIMAGE:
Serialise_glClearTexSubImage(0, 0, 0, 0, 0, 0, 0, 0, eGL_NONE, eGL_NONE, NULL);
break;
case POLYGON_MODE:
Serialise_glPolygonMode(eGL_NONE, eGL_NONE);
break;
case POLYGON_OFFSET:
Serialise_glPolygonOffset(0, 0);
break;
case POLYGON_OFFSET_CLAMP:
Serialise_glPolygonOffsetClampEXT(0, 0, 0);
break;
case CULL_FACE:
Serialise_glCullFace(eGL_NONE);
break;
case HINT:
Serialise_glHint(eGL_NONE, eGL_NONE);
break;
case ENABLE:
Serialise_glEnable(eGL_NONE);
break;
case DISABLE:
Serialise_glDisable(eGL_NONE);
break;
case ENABLEI:
Serialise_glEnablei(eGL_NONE, 0);
break;
case DISABLEI:
Serialise_glDisablei(eGL_NONE, 0);
break;
case FRONT_FACE:
Serialise_glFrontFace(eGL_NONE);
break;
case BLEND_FUNC:
Serialise_glBlendFunc(eGL_NONE, eGL_NONE);
break;
case BLEND_FUNCI:
Serialise_glBlendFunci(0, eGL_NONE, eGL_NONE);
break;
case BLEND_COLOR:
Serialise_glBlendColor(0, 0, 0, 0);
break;
case BLEND_FUNC_SEP:
Serialise_glBlendFuncSeparate(eGL_NONE, eGL_NONE, eGL_NONE, eGL_NONE);
break;
case BLEND_FUNC_SEPI:
Serialise_glBlendFuncSeparatei(0, eGL_NONE, eGL_NONE, eGL_NONE, eGL_NONE);
break;
case BLEND_EQ:
Serialise_glBlendEquation(eGL_NONE);
break;
case BLEND_EQI:
Serialise_glBlendEquationi(0, eGL_NONE);
break;
case BLEND_EQ_SEP:
Serialise_glBlendEquationSeparate(eGL_NONE, eGL_NONE);
break;
case BLEND_EQ_SEPI:
Serialise_glBlendEquationSeparatei(0, eGL_NONE, eGL_NONE);
break;
case BLEND_BARRIER:
Serialise_glBlendBarrierKHR();
break;
case LOGIC_OP:
Serialise_glLogicOp(eGL_NONE);
break;
case STENCIL_OP:
Serialise_glStencilOp(eGL_NONE, eGL_NONE, eGL_NONE);
break;
case STENCIL_OP_SEP:
Serialise_glStencilOpSeparate(eGL_NONE, eGL_NONE, eGL_NONE, eGL_NONE);
break;
case STENCIL_FUNC:
Serialise_glStencilFunc(eGL_NONE, 0, 0);
break;
case STENCIL_FUNC_SEP:
Serialise_glStencilFuncSeparate(eGL_NONE, eGL_NONE, 0, 0);
break;
case STENCIL_MASK:
Serialise_glStencilMask(0);
break;
case STENCIL_MASK_SEP:
Serialise_glStencilMaskSeparate(eGL_NONE, 0);
break;
case COLOR_MASK:
Serialise_glColorMask(0, 0, 0, 0);
break;
case COLOR_MASKI:
Serialise_glColorMaski(0, 0, 0, 0, 0);
break;
case SAMPLE_MASK:
Serialise_glSampleMaski(0, 0);
break;
case SAMPLE_COVERAGE:
Serialise_glSampleCoverage(0.0f, 0);
break;
case MIN_SAMPLE_SHADING:
Serialise_glMinSampleShading(0.0f);
break;
case RASTER_SAMPLES:
Serialise_glRasterSamplesEXT(0, 0);
break;
case DEPTH_FUNC:
Serialise_glDepthFunc(eGL_NONE);
break;
case DEPTH_MASK:
Serialise_glDepthMask(0);
break;
case DEPTH_RANGE:
Serialise_glDepthRange(0, 0);
break;
case DEPTH_RANGEF:
Serialise_glDepthRangef(0, 0);
break;
case DEPTH_RANGE_IDX:
Serialise_glDepthRangeIndexed(0, 0.0, 0.0);
break;
case DEPTH_RANGEARRAY:
Serialise_glDepthRangeArrayv(0, 0, NULL);
break;
case DEPTH_BOUNDS:
Serialise_glDepthBoundsEXT(0, 0);
break;
case CLIP_CONTROL:
Serialise_glClipControl(eGL_NONE, eGL_NONE);
break;
case PROVOKING_VERTEX:
Serialise_glProvokingVertex(eGL_NONE);
break;
case PRIMITIVE_RESTART:
Serialise_glPrimitiveRestartIndex(0);
break;
case PATCH_PARAMI:
Serialise_glPatchParameteri(eGL_NONE, 0);
break;
case PATCH_PARAMFV:
Serialise_glPatchParameterfv(eGL_NONE, NULL);
break;
case LINE_WIDTH:
Serialise_glLineWidth(0.0f);
break;
case POINT_SIZE:
Serialise_glPointSize(0.0f);
break;
case POINT_PARAMF:
Serialise_glPointParameterf(eGL_NONE, 0.0f);
break;
case POINT_PARAMFV:
Serialise_glPointParameterfv(eGL_NONE, NULL);
break;
case POINT_PARAMI:
Serialise_glPointParameteri(eGL_NONE, 0);
break;
case POINT_PARAMIV:
Serialise_glPointParameteriv(eGL_NONE, NULL);
break;
case VIEWPORT:
Serialise_glViewport(0, 0, 0, 0);
break;
case VIEWPORT_ARRAY:
Serialise_glViewportArrayv(0, 0, 0);
break;
case SCISSOR:
Serialise_glScissor(0, 0, 0, 0);
break;
case SCISSOR_ARRAY:
Serialise_glScissorArrayv(0, 0, 0);
break;
case BIND_VERTEXBUFFER:
Serialise_glVertexArrayBindVertexBufferEXT(0, 0, 0, 0, 0);
break;
case BIND_VERTEXBUFFERS:
Serialise_glVertexArrayVertexBuffers(0, 0, 0, NULL, NULL, NULL);
break;
case VERTEXBINDINGDIVISOR:
Serialise_glVertexArrayVertexBindingDivisorEXT(0, 0, 0);
break;
case DISPATCH_COMPUTE:
Serialise_glDispatchCompute(0, 0, 0);
break;
case DISPATCH_COMPUTE_GROUP_SIZE:
Serialise_glDispatchComputeGroupSizeARB(0, 0, 0, 0, 0, 0);
break;
case DISPATCH_COMPUTE_INDIRECT:
Serialise_glDispatchComputeIndirect(0);
break;
case MEMORY_BARRIER:
Serialise_glMemoryBarrier(0);
break;
case MEMORY_BARRIER_BY_REGION:
Serialise_glMemoryBarrierByRegion(0);
break;
case TEXTURE_BARRIER:
Serialise_glTextureBarrier();
break;
case DRAWARRAYS:
Serialise_glDrawArrays(eGL_NONE, 0, 0);
break;
case DRAWARRAYS_INDIRECT:
Serialise_glDrawArraysIndirect(eGL_NONE, 0);
break;
case DRAWARRAYS_INSTANCED:
Serialise_glDrawArraysInstanced(eGL_NONE, 0, 0, 0);
break;
case DRAWARRAYS_INSTANCEDBASEINSTANCE:
Serialise_glDrawArraysInstancedBaseInstance(eGL_NONE, 0, 0, 0, 0);
break;
case DRAWELEMENTS:
Serialise_glDrawElements(eGL_NONE, 0, eGL_NONE, NULL);
break;
case DRAWELEMENTS_INDIRECT:
Serialise_glDrawElementsIndirect(eGL_NONE, eGL_NONE, 0);
break;
case DRAWRANGEELEMENTS:
Serialise_glDrawRangeElements(eGL_NONE, 0, 0, 0, eGL_NONE, NULL);
break;
case DRAWRANGEELEMENTSBASEVERTEX:
Serialise_glDrawRangeElementsBaseVertex(eGL_NONE, 0, 0, 0, eGL_NONE, NULL, 0);
break;
case DRAWELEMENTS_INSTANCED:
Serialise_glDrawElementsInstanced(eGL_NONE, 0, eGL_NONE, NULL, 0);
break;
case DRAWELEMENTS_INSTANCEDBASEINSTANCE:
Serialise_glDrawElementsInstancedBaseInstance(eGL_NONE, 0, eGL_NONE, NULL, 0, 0);
break;
case DRAWELEMENTS_BASEVERTEX:
Serialise_glDrawElementsBaseVertex(eGL_NONE, 0, eGL_NONE, NULL, 0);
break;
case DRAWELEMENTS_INSTANCEDBASEVERTEX:
Serialise_glDrawElementsInstancedBaseVertex(eGL_NONE, 0, eGL_NONE, NULL, 0, 0);
break;
case DRAWELEMENTS_INSTANCEDBASEVERTEXBASEINSTANCE:
Serialise_glDrawElementsInstancedBaseVertexBaseInstance(eGL_NONE, 0, eGL_NONE, NULL, 0, 0, 0);
break;
case DRAW_FEEDBACK:
Serialise_glDrawTransformFeedback(eGL_NONE, 0);
break;
case DRAW_FEEDBACK_INSTANCED:
Serialise_glDrawTransformFeedbackInstanced(eGL_NONE, 0, 0);
break;
case DRAW_FEEDBACK_STREAM:
Serialise_glDrawTransformFeedbackStream(eGL_NONE, 0, 0);
break;
case DRAW_FEEDBACK_STREAM_INSTANCED:
Serialise_glDrawTransformFeedbackStreamInstanced(eGL_NONE, 0, 0, 0);
break;
case MULTI_DRAWARRAYS:
Serialise_glMultiDrawArrays(eGL_NONE, NULL, NULL, 0);
break;
case MULTI_DRAWELEMENTS:
Serialise_glMultiDrawElements(eGL_NONE, NULL, eGL_NONE, NULL, 0);
break;
case MULTI_DRAWELEMENTSBASEVERTEX:
Serialise_glMultiDrawElementsBaseVertex(eGL_NONE, NULL, eGL_NONE, NULL, 0, NULL);
break;
case MULTI_DRAWARRAYS_INDIRECT:
Serialise_glMultiDrawArraysIndirect(eGL_NONE, NULL, 0, 0);
break;
case MULTI_DRAWELEMENTS_INDIRECT:
Serialise_glMultiDrawElementsIndirect(eGL_NONE, eGL_NONE, NULL, 0, 0);
break;
case MULTI_DRAWARRAYS_INDIRECT_COUNT:
Serialise_glMultiDrawArraysIndirectCountARB(eGL_NONE, 0, 0, 0, 0);
break;
case MULTI_DRAWELEMENTS_INDIRECT_COUNT:
Serialise_glMultiDrawElementsIndirectCountARB(eGL_NONE, eGL_NONE, 0, 0, 0, 0);
break;
case GEN_FRAMEBUFFERS:
Serialise_glGenFramebuffers(0, NULL);
break;
case CREATE_FRAMEBUFFERS:
Serialise_glCreateFramebuffers(0, NULL);
break;
case FRAMEBUFFER_TEX:
Serialise_glNamedFramebufferTextureEXT(0, eGL_NONE, 0, 0);
break;
case FRAMEBUFFER_TEX1D:
Serialise_glNamedFramebufferTexture1DEXT(0, eGL_NONE, eGL_NONE, 0, 0);
break;
case FRAMEBUFFER_TEX2D:
Serialise_glNamedFramebufferTexture2DEXT(0, eGL_NONE, eGL_NONE, 0, 0);
break;
case FRAMEBUFFER_TEX3D:
Serialise_glNamedFramebufferTexture3DEXT(0, eGL_NONE, eGL_NONE, 0, 0, 0);
break;
case FRAMEBUFFER_RENDBUF:
Serialise_glNamedFramebufferRenderbufferEXT(0, eGL_NONE, eGL_NONE, 0);
break;
case FRAMEBUFFER_TEXLAYER:
Serialise_glNamedFramebufferTextureLayerEXT(0, eGL_NONE, 0, 0, 0);
break;
case FRAMEBUFFER_PARAM:
Serialise_glNamedFramebufferParameteriEXT(0, eGL_NONE, 0);
break;
case READ_BUFFER:
Serialise_glFramebufferReadBufferEXT(0, eGL_NONE);
break;
case BIND_FRAMEBUFFER:
Serialise_glBindFramebuffer(eGL_NONE, 0);
break;
case DRAW_BUFFER:
Serialise_glFramebufferDrawBufferEXT(0, eGL_NONE);
break;
case DRAW_BUFFERS:
Serialise_glFramebufferDrawBuffersEXT(0, 0, NULL);
break;
case BLIT_FRAMEBUFFER:
Serialise_glBlitNamedFramebuffer(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, eGL_NONE);
break;
case GEN_RENDERBUFFERS:
Serialise_glGenRenderbuffers(0, NULL);
break;
case CREATE_RENDERBUFFERS:
Serialise_glCreateRenderbuffers(0, NULL);
break;
case RENDERBUFFER_STORAGE:
Serialise_glNamedRenderbufferStorageEXT(0, eGL_NONE, 0, 0);
break;
case RENDERBUFFER_STORAGEMS:
Serialise_glNamedRenderbufferStorageMultisampleEXT(0, 0, eGL_NONE, 0, 0);
break;
case GEN_SAMPLERS:
Serialise_glGenSamplers(0, NULL);
break;
case CREATE_SAMPLERS:
Serialise_glCreateSamplers(0, NULL);
break;
case SAMPLER_PARAMETERI:
Serialise_glSamplerParameteri(0, eGL_NONE, 0);
break;
case SAMPLER_PARAMETERF:
Serialise_glSamplerParameterf(0, eGL_NONE, 0);
break;
case SAMPLER_PARAMETERIV:
Serialise_glSamplerParameteriv(0, eGL_NONE, NULL);
break;
case SAMPLER_PARAMETERFV:
Serialise_glSamplerParameterfv(0, eGL_NONE, NULL);
break;
case SAMPLER_PARAMETERIIV:
Serialise_glSamplerParameterIiv(0, eGL_NONE, NULL);
break;
case SAMPLER_PARAMETERIUIV:
Serialise_glSamplerParameterIuiv(0, eGL_NONE, NULL);
break;
case BIND_SAMPLER:
Serialise_glBindSampler(0, 0);
break;
case BIND_SAMPLERS:
Serialise_glBindSamplers(0, 0, NULL);
break;
case GEN_BUFFER:
Serialise_glGenBuffers(0, NULL);
break;
case CREATE_BUFFER:
Serialise_glCreateBuffers(0, NULL);
break;
case BIND_BUFFER:
Serialise_glBindBuffer(eGL_NONE, 0);
break;
case BIND_BUFFER_BASE:
Serialise_glBindBufferBase(eGL_NONE, 0, 0);
break;
case BIND_BUFFER_RANGE:
Serialise_glBindBufferRange(eGL_NONE, 0, 0, 0, 0);
break;
case BIND_BUFFERS_BASE:
Serialise_glBindBuffersBase(eGL_NONE, 0, 0, NULL);
break;
case BIND_BUFFERS_RANGE:
Serialise_glBindBuffersRange(eGL_NONE, 0, 0, NULL, NULL, NULL);
break;
case BUFFERSTORAGE:
Serialise_glNamedBufferStorageEXT(0, 0, NULL, 0);
break;
case BUFFERDATA:
Serialise_glNamedBufferDataEXT(eGL_NONE, 0, NULL, eGL_NONE);
break;
case BUFFERSUBDATA:
Serialise_glNamedBufferSubDataEXT(0, 0, 0, NULL);
break;
case COPYBUFFERSUBDATA:
Serialise_glNamedCopyBufferSubDataEXT(0, 0, 0, 0, 0);
break;
case UNMAP:
Serialise_glUnmapNamedBufferEXT(eGL_NONE);
break;
case FLUSHMAP:
Serialise_glFlushMappedNamedBufferRangeEXT(0, 0, 0);
break;
case GEN_VERTEXARRAY:
Serialise_glGenVertexArrays(0, NULL);
break;
case CREATE_VERTEXARRAY:
Serialise_glCreateVertexArrays(0, NULL);
break;
case BIND_VERTEXARRAY:
Serialise_glBindVertexArray(0);
break;
case VERTEXATTRIBPOINTER:
Serialise_glVertexArrayVertexAttribOffsetEXT(0, 0, 0, 0, eGL_NONE, 0, 0, 0);
break;
case VERTEXATTRIBIPOINTER:
Serialise_glVertexArrayVertexAttribIOffsetEXT(0, 0, 0, 0, eGL_NONE, 0, 0);
break;
case VERTEXATTRIBLPOINTER:
Serialise_glVertexArrayVertexAttribLOffsetEXT(0, 0, 0, 0, eGL_NONE, 0, 0);
break;
case ENABLEVERTEXATTRIBARRAY:
Serialise_glEnableVertexArrayAttribEXT(0, 0);
break;
case DISABLEVERTEXATTRIBARRAY:
Serialise_glDisableVertexArrayAttribEXT(0, 0);
break;
case VERTEXATTRIB_GENERIC:
Serialise_glVertexAttrib(0, 0, eGL_NONE, GL_FALSE, NULL, Attrib_packed);
break;
case VERTEXATTRIBFORMAT:
Serialise_glVertexArrayVertexAttribFormatEXT(0, 0, 0, eGL_NONE, 0, 0);
break;
case VERTEXATTRIBIFORMAT:
Serialise_glVertexArrayVertexAttribIFormatEXT(0, 0, 0, eGL_NONE, 0);
break;
case VERTEXATTRIBLFORMAT:
Serialise_glVertexArrayVertexAttribLFormatEXT(0, 0, 0, eGL_NONE, 0);
break;
case VERTEXATTRIBDIVISOR:
Serialise_glVertexArrayVertexAttribDivisorEXT(0, 0, 0);
break;
case VERTEXATTRIBBINDING:
Serialise_glVertexArrayVertexAttribBindingEXT(0, 0, 0);
break;
case VAO_ELEMENT_BUFFER:
Serialise_glVertexArrayElementBuffer(0, 0);
break;
case FEEDBACK_BUFFER_BASE:
Serialise_glTransformFeedbackBufferBase(0, 0, 0);
break;
case FEEDBACK_BUFFER_RANGE:
Serialise_glTransformFeedbackBufferRange(0, 0, 0, 0, 0);
break;
case OBJECT_LABEL:
Serialise_glObjectLabel(eGL_NONE, 0, 0, NULL);
break;
case BEGIN_EVENT:
Serialise_glPushDebugGroup(eGL_NONE, 0, 0, NULL);
break;
case SET_MARKER:
Serialise_glDebugMessageInsert(eGL_NONE, eGL_NONE, 0, eGL_NONE, 0, NULL);
break;
case END_EVENT:
Serialise_glPopDebugGroup();
break;
case CAPTURE_SCOPE:
Serialise_CaptureScope(offset);
break;
case CONTEXT_CAPTURE_HEADER:
// normally this would be handled as a special case when we start processing the frame,
// but it can be emitted mid-frame if MakeCurrent is called on a different context.
// when processed here, we always want to apply the contents
Serialise_BeginCaptureFrame(true);
break;
case CONTEXT_CAPTURE_FOOTER:
{
bool HasCallstack = false;
m_pSerialiser->Serialise("HasCallstack", HasCallstack);
if(HasCallstack)
{
size_t numLevels = 0;
uint64_t *stack = NULL;
m_pSerialiser->SerialisePODArray("callstack", stack, numLevels);
m_pSerialiser->SetCallstack(stack, numLevels);
SAFE_DELETE_ARRAY(stack);
}
if(m_State == READING)
{
AddEvent(CONTEXT_CAPTURE_FOOTER, "SwapBuffers()");
FetchDrawcall draw;
draw.name = "SwapBuffers()";
draw.flags |= eDraw_Present;
draw.copyDestination = GetResourceManager()->GetOriginalID(GetResourceManager()->GetID(TextureRes(GetCtx(), m_FakeBB_Color)));
AddDrawcall(draw, true);
}
}
break;
default:
// ignore system chunks
if((int)context == (int)INITIAL_CONTENTS)
GetResourceManager()->Serialise_InitialState(ResourceId(), GLResource(MakeNullResource));
else if((int)context < (int)FIRST_CHUNK_ID)
m_pSerialiser->SkipCurrentChunk();
else
RDCERR("Unrecognised Chunk type %d", context);
break;
}
}
void WrappedOpenGL::ContextReplayLog(LogState readType, uint32_t startEventID, uint32_t endEventID, bool partial)
{
m_State = readType;
m_DoStateVerify = true;
GLChunkType header = (GLChunkType)m_pSerialiser->PushContext(NULL, NULL, 1, false);
RDCASSERTEQUAL(header, CONTEXT_CAPTURE_HEADER);
if(m_State == EXECUTING && !partial)
{
for(size_t i=0; i < 8; i++)
{
GLenum q = QueryEnum(i);
if(q == eGL_NONE) break;
for(int j=0; j < 8; j++)
{
if(m_ActiveQueries[i][j])
{
m_Real.glEndQueryIndexed(q, j);
m_ActiveQueries[i][j] = false;
}
}
}
if(m_ActiveConditional)
{
m_Real.glEndConditionalRender();
m_ActiveConditional = false;
}
if(m_ActiveFeedback)
{
m_Real.glEndTransformFeedback();
m_ActiveFeedback = false;
}
}
Serialise_BeginCaptureFrame(!partial);
m_pSerialiser->PopContext(header);
m_CurEvents.clear();
if(m_State == EXECUTING)
{
FetchAPIEvent ev = GetEvent(startEventID);
m_CurEventID = ev.eventID;
m_pSerialiser->SetOffset(ev.fileOffset);
m_FirstEventID = startEventID;
m_LastEventID = endEventID;
}
else if(m_State == READING)
{
m_CurEventID = 1;
m_CurDrawcallID = 1;
m_FirstEventID = 0;
m_LastEventID = ~0U;
}
GetResourceManager()->MarkInFrame(true);
uint64_t startOffset = m_pSerialiser->GetOffset();
for(;;)
{
if(m_State == EXECUTING && m_CurEventID > endEventID)
{
// we can just break out if we've done all the events desired.
break;
}
uint64_t offset = m_pSerialiser->GetOffset();
GLChunkType chunktype = (GLChunkType)m_pSerialiser->PushContext(NULL, NULL, 1, false);
ContextProcessChunk(offset, chunktype, false);
RenderDoc::Inst().SetProgress(FrameEventsRead, float(offset - startOffset)/float(m_pSerialiser->GetSize()));
// for now just abort after capture scope. Really we'd need to support multiple frames
// but for now this will do.
if(chunktype == CONTEXT_CAPTURE_FOOTER)
break;
m_CurEventID++;
}
if(m_State == READING)
{
GetFrameRecord().drawcallList = m_ParentDrawcall.Bake();
GetFrameRecord().frameInfo.debugMessages = GetDebugMessages();
SetupDrawcallPointers(&m_Drawcalls, GetFrameRecord().frameInfo.immContextId, GetFrameRecord().drawcallList, NULL, NULL);
// it's easier to remove duplicate usages here than check it as we go.
// this means if textures are bound in multiple places in the same draw
// we don't have duplicate uses
for(auto it = m_ResourceUses.begin(); it != m_ResourceUses.end(); ++it)
{
vector<EventUsage> &v = it->second;
std::sort(v.begin(), v.end());
v.erase( std::unique(v.begin(), v.end()), v.end() );
}
}
GetResourceManager()->MarkInFrame(false);
m_State = READING;
m_DoStateVerify = false;
}
void WrappedOpenGL::ContextProcessChunk(uint64_t offset, GLChunkType chunk, bool forceExecute)
{
m_CurChunkOffset = offset;
WrappedOpenGL *context = this;
LogState state = context->m_State;
if(forceExecute)
context->m_State = EXECUTING;
else
context->m_State = m_State;
m_AddedDrawcall = false;
ProcessChunk(offset, chunk);
m_pSerialiser->PopContext(chunk);
if(context->m_State == READING && chunk == SET_MARKER)
{
// no push/pop necessary
}
else if(context->m_State == READING && chunk == BEGIN_EVENT)
{
// push down the drawcallstack to the latest drawcall
context->m_DrawcallStack.push_back(&context->m_DrawcallStack.back()->children.back());
}
else if(context->m_State == READING && chunk == END_EVENT)
{
// refuse to pop off further than the root drawcall (mismatched begin/end events e.g.)
if(context->m_DrawcallStack.size() > 1)
context->m_DrawcallStack.pop_back();
}
else if(context->m_State == READING)
{
if(!m_AddedDrawcall)
context->AddEvent(chunk, m_pSerialiser->GetDebugStr());
}
m_AddedDrawcall = false;
if(forceExecute)
context->m_State = state;
}
void WrappedOpenGL::AddUsage(FetchDrawcall d)
{
if((d.flags & (eDraw_Drawcall|eDraw_Dispatch)) == 0)
return;
const GLHookSet &gl = m_Real;
GLResourceManager *rm = GetResourceManager();
void *ctx = GetCtx();
uint32_t e = d.eventID;
//////////////////////////////
// Input
if(d.flags & eDraw_UseIBuffer)
{
GLuint ibuffer = 0;
gl.glGetIntegerv(eGL_ELEMENT_ARRAY_BUFFER_BINDING, (GLint*)&ibuffer);
if(ibuffer)
m_ResourceUses[rm->GetID(BufferRes(ctx, ibuffer))].push_back(EventUsage(e, eUsage_IndexBuffer));
}
// Vertex buffers and attributes
GLint numVBufferBindings = 16;
gl.glGetIntegerv(eGL_MAX_VERTEX_ATTRIB_BINDINGS, &numVBufferBindings);
for(GLuint i=0; i < (GLuint)numVBufferBindings; i++)
{
GLuint buffer = GetBoundVertexBuffer(m_Real, i);
if(buffer)
m_ResourceUses[rm->GetID(BufferRes(ctx, buffer))].push_back(EventUsage(e, eUsage_VertexBuffer));
}
//////////////////////////////
// Shaders
{
GLRenderState rs(&m_Real, NULL, READING);
rs.FetchState(ctx, this);
ShaderReflection *refl[6] = { NULL };
ShaderBindpointMapping mapping[6];
GLuint curProg = 0;
gl.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint*)&curProg);
if(curProg == 0)
{
gl.glGetIntegerv(eGL_PROGRAM_PIPELINE_BINDING, (GLint*)&curProg);
if(curProg == 0)
{
// no program bound at this draw
}
else
{
auto &pipeDetails = m_Pipelines[rm->GetID(ProgramPipeRes(ctx, curProg))];
for(size_t i=0; i < ARRAY_COUNT(pipeDetails.stageShaders); i++)
{
if(pipeDetails.stageShaders[i] != ResourceId())
{
curProg = rm->GetCurrentResource(pipeDetails.stagePrograms[i]).name;
refl[i] = &m_Shaders[pipeDetails.stageShaders[i]].reflection;
GetBindpointMapping(m_Real, curProg, (int)i, refl[i], mapping[i]);
}
}
}
}
else
{
auto &progDetails = m_Programs[rm->GetID(ProgramRes(ctx, curProg))];
for(size_t i=0; i < ARRAY_COUNT(progDetails.stageShaders); i++)
{
if(progDetails.stageShaders[i] != ResourceId())
{
refl[i] = &m_Shaders[progDetails.stageShaders[i]].reflection;
GetBindpointMapping(m_Real, curProg, (int)i, refl[i], mapping[i]);
}
}
}
for(size_t i=0; i < ARRAY_COUNT(refl); i++)
{
EventUsage cb = EventUsage(e, ResourceUsage(eUsage_VS_Constants + i));
EventUsage res = EventUsage(e, ResourceUsage(eUsage_VS_Resource + i));
EventUsage rw = EventUsage(e, ResourceUsage(eUsage_VS_RWResource + i));
if(refl[i])
{
for(int32_t c=0; c < refl[i]->ConstantBlocks.count; c++)
{
if(!refl[i]->ConstantBlocks[c].bufferBacked) continue;
if(refl[i]->ConstantBlocks[c].bindPoint < 0 ||
refl[i]->ConstantBlocks[c].bindPoint >= mapping[i].ConstantBlocks.count) continue;
int32_t bind = mapping[i].ConstantBlocks[ refl[i]->ConstantBlocks[c].bindPoint ].bind;
if(rs.UniformBinding[bind].name)
m_ResourceUses[rm->GetID(BufferRes(ctx, rs.UniformBinding[bind].name))].push_back(cb);
}
for(int32_t r=0; r < refl[i]->ReadWriteResources.count; r++)
{
int32_t bind = mapping[i].ReadWriteResources[ refl[i]->ReadWriteResources[r].bindPoint ].bind;
if(refl[i]->ReadWriteResources[r].IsTexture)
{
if(rs.Images[bind].name)
m_ResourceUses[rm->GetID(TextureRes(ctx, rs.Images[bind].name))].push_back(rw);
}
else
{
if(refl[i]->ReadWriteResources[r].variableType.descriptor.cols == 1 &&
refl[i]->ReadWriteResources[r].variableType.descriptor.rows == 1 &&
refl[i]->ReadWriteResources[r].variableType.descriptor.type == eVar_UInt)
{
if(rs.AtomicCounter[bind].name)
m_ResourceUses[rm->GetID(BufferRes(ctx, rs.AtomicCounter[bind].name))].push_back(rw);
}
else
{
if(rs.ShaderStorage[bind].name)
m_ResourceUses[rm->GetID(BufferRes(ctx, rs.ShaderStorage[bind].name))].push_back(rw);
}
}
}
for(int32_t r=0; r < refl[i]->ReadOnlyResources.count; r++)
{
int32_t bind = mapping[i].ReadOnlyResources[ refl[i]->ReadOnlyResources[r].bindPoint ].bind;
uint32_t *texList = NULL;
int32_t listSize = 0;
switch(refl[i]->ReadOnlyResources[r].resType)
{
case eResType_None:
texList = NULL;
break;
case eResType_Buffer:
texList = rs.TexBuffer;
listSize = sizeof(rs.TexBuffer);
break;
case eResType_Texture1D:
texList = rs.Tex1D;
listSize = sizeof(rs.Tex1D);
break;
case eResType_Texture1DArray:
texList = rs.Tex1DArray;
listSize = sizeof(rs.Tex1DArray);
break;
case eResType_Texture2D:
texList = rs.Tex2D;
listSize = sizeof(rs.Tex2D);
break;
case eResType_TextureRect:
texList = rs.TexRect;
listSize = sizeof(rs.TexRect);
break;
case eResType_Texture2DArray:
texList = rs.Tex2DArray;
listSize = sizeof(rs.Tex2DArray);
break;
case eResType_Texture2DMS:
texList = rs.Tex2DMS;
listSize = sizeof(rs.Tex2DMS);
break;
case eResType_Texture2DMSArray:
texList = rs.Tex2DMSArray;
listSize = sizeof(rs.Tex2DMSArray);
break;
case eResType_Texture3D:
texList = rs.Tex3D;
listSize = sizeof(rs.Tex3D);
break;
case eResType_TextureCube:
texList = rs.TexCube;
listSize = sizeof(rs.TexCube);
break;
case eResType_TextureCubeArray:
texList = rs.TexCubeArray;
listSize = sizeof(rs.TexCubeArray);
break;
case eResType_Count:
RDCERR("Invalid shader resource type");
break;
}
if(texList != NULL && bind >= 0 && bind < listSize && texList[bind] != 0)
m_ResourceUses[rm->GetID(TextureRes(ctx, texList[bind]))].push_back(res);
}
}
}
}
//////////////////////////////
// Feedback
GLint maxCount = 0;
gl.glGetIntegerv(eGL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &maxCount);
for(int i=0; i < maxCount; i++)
{
GLuint buffer = 0;
gl.glGetIntegeri_v(eGL_TRANSFORM_FEEDBACK_BUFFER_BINDING, i, (GLint*)&buffer);
if(buffer)
m_ResourceUses[rm->GetID(BufferRes(ctx, buffer))].push_back(EventUsage(e, eUsage_SO));
}
//////////////////////////////
// FBO
GLint numCols = 8;
gl.glGetIntegerv(eGL_MAX_COLOR_ATTACHMENTS, &numCols);
GLuint attachment = 0;
GLenum type = eGL_TEXTURE;
for(GLint i=0; i < numCols; i++)
{
type = eGL_TEXTURE;
gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0+i), eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&attachment);
gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0+i), eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint*)&type);
if(attachment)
{
if(type == eGL_TEXTURE)
m_ResourceUses[rm->GetID(TextureRes(ctx, attachment))].push_back(EventUsage(e, eUsage_ColourTarget));
else
m_ResourceUses[rm->GetID(RenderbufferRes(ctx, attachment))].push_back(EventUsage(e, eUsage_ColourTarget));
}
}
gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&attachment);
gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint*)&type);
if(attachment)
{
if(type == eGL_TEXTURE)
m_ResourceUses[rm->GetID(TextureRes(ctx, attachment))].push_back(EventUsage(e, eUsage_DepthStencilTarget));
else
m_ResourceUses[rm->GetID(RenderbufferRes(ctx, attachment))].push_back(EventUsage(e, eUsage_DepthStencilTarget));
}
gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&attachment);
gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint*)&type);
if(attachment)
{
if(type == eGL_TEXTURE)
m_ResourceUses[rm->GetID(TextureRes(ctx, attachment))].push_back(EventUsage(e, eUsage_DepthStencilTarget));
else
m_ResourceUses[rm->GetID(RenderbufferRes(ctx, attachment))].push_back(EventUsage(e, eUsage_DepthStencilTarget));
}
}
void WrappedOpenGL::AddDrawcall(FetchDrawcall d, bool hasEvents)
{
if(d.context == ResourceId()) d.context = GetResourceManager()->GetOriginalID(m_ContextResourceID);
m_AddedDrawcall = true;
WrappedOpenGL *context = this;
FetchDrawcall draw = d;
draw.eventID = m_CurEventID;
draw.drawcallID = m_CurDrawcallID;
GLuint curCol[8] = { 0 };
GLuint curDepth = 0;
{
GLint numCols = 8;
m_Real.glGetIntegerv(eGL_MAX_COLOR_ATTACHMENTS, &numCols);
RDCEraseEl(draw.outputs);
for(GLint i=0; i < RDCMIN(numCols, 8); i++)
{
m_Real.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0+i), eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&curCol[i]);
draw.outputs[i] = GetResourceManager()->GetID(TextureRes(GetCtx(), curCol[i]));
}
m_Real.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&curDepth);
draw.depthOut = GetResourceManager()->GetID(TextureRes(GetCtx(), curDepth));
}
// markers don't increment drawcall ID
if((draw.flags & (eDraw_SetMarker|eDraw_PushMarker|eDraw_MultiDraw)) == 0)
m_CurDrawcallID++;
if(hasEvents)
{
vector<FetchAPIEvent> evs;
evs.reserve(m_CurEvents.size());
for(size_t i=0; i < m_CurEvents.size(); )
{
if(m_CurEvents[i].context == draw.context)
{
evs.push_back(m_CurEvents[i]);
m_CurEvents.erase(m_CurEvents.begin()+i);
}
else
{
i++;
}
}
draw.events = evs;
}
AddUsage(draw);
// should have at least the root drawcall here, push this drawcall
// onto the back's children list.
if(!context->m_DrawcallStack.empty())
{
DrawcallTreeNode node(draw);
node.children.insert(node.children.begin(), draw.children.elems, draw.children.elems+draw.children.count);
context->m_DrawcallStack.back()->children.push_back(node);
}
else
RDCERR("Somehow lost drawcall stack!");
}
void WrappedOpenGL::AddEvent(GLChunkType type, string description, ResourceId ctx)
{
if(ctx == ResourceId()) ctx = GetResourceManager()->GetOriginalID(m_ContextResourceID);
FetchAPIEvent apievent;
apievent.context = ctx;
apievent.fileOffset = m_CurChunkOffset;
apievent.eventID = m_CurEventID;
apievent.eventDesc = description;
Callstack::Stackwalk *stack = m_pSerialiser->GetLastCallstack();
if(stack)
{
create_array(apievent.callstack, stack->NumLevels());
memcpy(apievent.callstack.elems, stack->GetAddrs(), sizeof(uint64_t)*stack->NumLevels());
}
m_CurEvents.push_back(apievent);
if(m_State == READING)
m_Events.push_back(apievent);
}
FetchAPIEvent WrappedOpenGL::GetEvent(uint32_t eventID)
{
for(size_t i=m_Events.size()-1; i > 0; i--)
{
if(m_Events[i].eventID <= eventID)
return m_Events[i];
}
return m_Events[0];
}
const FetchDrawcall *WrappedOpenGL::GetDrawcall(uint32_t eventID)
{
if(eventID >= m_Drawcalls.size())
return NULL;
return m_Drawcalls[eventID];
}
void WrappedOpenGL::ReplayLog(uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType)
{
uint64_t offs = m_FrameRecord.frameInfo.fileOffset;
m_pSerialiser->SetOffset(offs);
bool partial = true;
if(startEventID == 0 && (replayType == eReplay_WithoutDraw || replayType == eReplay_Full))
{
startEventID = m_FrameRecord.frameInfo.firstEvent;
partial = false;
}
GLChunkType header = (GLChunkType)m_pSerialiser->PushContext(NULL, NULL, 1, false);
RDCASSERTEQUAL(header, CAPTURE_SCOPE);
m_pSerialiser->SkipCurrentChunk();
m_pSerialiser->PopContext(header);
if(!partial)
{
GetResourceManager()->ApplyInitialContents();
GetResourceManager()->ReleaseInFrameResources();
}
{
if(replayType == eReplay_Full)
ContextReplayLog(EXECUTING, startEventID, endEventID, partial);
else if(replayType == eReplay_WithoutDraw)
ContextReplayLog(EXECUTING, startEventID, RDCMAX(1U,endEventID)-1, partial);
else if(replayType == eReplay_OnlyDraw)
ContextReplayLog(EXECUTING, endEventID, endEventID, partial);
else
RDCFATAL("Unexpected replay type");
}
}