mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-14 10:36:43 +00:00
1432 lines
55 KiB
C++
1432 lines
55 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2019-2023 Baldur Karlsson
|
|
*
|
|
* 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 <algorithm>
|
|
#include "data/glsl_shaders.h"
|
|
#include "gl_common.h"
|
|
#include "gl_driver.h"
|
|
#include "gl_replay.h"
|
|
|
|
namespace
|
|
{
|
|
bool isDirectWrite(ResourceUsage usage)
|
|
{
|
|
return ((usage >= ResourceUsage::VS_RWResource && usage <= ResourceUsage::CS_RWResource) ||
|
|
usage == ResourceUsage::CopyDst || usage == ResourceUsage::Copy ||
|
|
usage == ResourceUsage::Resolve || usage == ResourceUsage::ResolveDst ||
|
|
usage == ResourceUsage::GenMips);
|
|
}
|
|
|
|
struct CopyFramebuffer
|
|
{
|
|
GLuint framebufferId;
|
|
GLuint colorTextureId;
|
|
GLuint dsTextureId;
|
|
GLuint depthTextureId;
|
|
GLuint stencilTextureId;
|
|
GLuint stencilViewId;
|
|
};
|
|
|
|
struct FramebufferKey
|
|
{
|
|
GLenum depthFormat;
|
|
GLenum stencilFormat;
|
|
uint32_t numSamples;
|
|
bool operator<(const FramebufferKey &other) const
|
|
{
|
|
return depthFormat < other.depthFormat ||
|
|
(depthFormat == other.depthFormat && stencilFormat < other.stencilFormat) ||
|
|
(depthFormat == other.depthFormat && stencilFormat == other.stencilFormat &&
|
|
numSamples < other.numSamples);
|
|
}
|
|
};
|
|
|
|
struct GLPixelHistoryResources
|
|
{
|
|
// Used for offscreen rendering for draw call events.
|
|
GLuint fullPrecisionColorImage;
|
|
GLuint fullPrecisionDsImage;
|
|
GLuint fullPrecisionFrameBuffer;
|
|
GLuint primitiveIdFragmentShader;
|
|
GLuint primitiveIdFragmentShaderSPIRV;
|
|
GLuint msCopyComputeProgram;
|
|
GLuint msCopyDepthComputeProgram;
|
|
GLuint msCopyDstBuffer;
|
|
GLuint msCopyUniformBlockBuffer;
|
|
std::unordered_map<GLuint, GLuint> programs;
|
|
std::map<FramebufferKey, CopyFramebuffer> copyFramebuffers;
|
|
};
|
|
|
|
enum class OpenGLTest
|
|
{
|
|
FaceCulling,
|
|
ScissorTest,
|
|
StencilTest,
|
|
DepthTest,
|
|
SampleMask,
|
|
NumTests
|
|
};
|
|
|
|
enum class PerFragmentQueryType
|
|
{
|
|
ShaderOut,
|
|
PostMod,
|
|
PrimitiveId
|
|
};
|
|
|
|
GLuint GetPrimitiveIdProgram(WrappedOpenGL *driver, GLReplay *replay,
|
|
GLPixelHistoryResources &resources, GLuint currentProgram)
|
|
{
|
|
auto programIterator = resources.programs.find(currentProgram);
|
|
if(programIterator != resources.programs.end())
|
|
{
|
|
return programIterator->second;
|
|
}
|
|
|
|
GLRenderState rs;
|
|
rs.FetchState(driver);
|
|
|
|
GLuint primitiveIdProgram = driver->glCreateProgram();
|
|
replay->CreateFragmentShaderReplacementProgram(
|
|
rs.Program.name, primitiveIdProgram, rs.Pipeline.name, resources.primitiveIdFragmentShader,
|
|
resources.primitiveIdFragmentShaderSPIRV);
|
|
|
|
return primitiveIdProgram;
|
|
}
|
|
|
|
// Returns a Framebuffer that has the same depth and stencil formats of the currently bound
|
|
// framebuffer
|
|
// so that you can blit from the current bound framebuffer into the new framebuffer
|
|
const CopyFramebuffer &getCopyFramebuffer(WrappedOpenGL *driver,
|
|
std::map<FramebufferKey, CopyFramebuffer> ©Framebuffers,
|
|
uint32_t numSamples, uint32_t numEvents,
|
|
GLenum depthFormat, GLenum stencilFormat)
|
|
{
|
|
bool multisampled = numSamples > 1;
|
|
|
|
GLuint curDepth;
|
|
GLint depthType;
|
|
GLuint curStencil;
|
|
GLint stencilType;
|
|
|
|
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
|
|
(GLint *)&curDepth);
|
|
|
|
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthType);
|
|
|
|
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
|
|
(GLint *)&curStencil);
|
|
|
|
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &stencilType);
|
|
|
|
auto it = copyFramebuffers.find({depthFormat, stencilFormat, numSamples});
|
|
if(it != copyFramebuffers.end())
|
|
{
|
|
return it->second;
|
|
}
|
|
|
|
GLint savedReadFramebuffer, savedDrawFramebuffer;
|
|
driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer);
|
|
driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer);
|
|
|
|
CopyFramebuffer copyFramebuffer;
|
|
RDCEraseEl(copyFramebuffer);
|
|
// Allocate a framebuffer that will render to the textures
|
|
driver->glGenFramebuffers(1, ©Framebuffer.framebufferId);
|
|
driver->glBindFramebuffer(eGL_FRAMEBUFFER, copyFramebuffer.framebufferId);
|
|
|
|
// Allocate a texture for the pixel history colour values
|
|
GLenum textureTarget = multisampled ? eGL_TEXTURE_2D_MULTISAMPLE : eGL_TEXTURE_2D;
|
|
driver->glGenTextures(1, ©Framebuffer.colorTextureId);
|
|
driver->CreateTextureImage(copyFramebuffer.colorTextureId, eGL_RGBA32F, eGL_NONE, eGL_NONE,
|
|
textureTarget, 2, numEvents, 1, 1, numSamples, 1);
|
|
driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0,
|
|
copyFramebuffer.colorTextureId, 0);
|
|
|
|
// Allocate a texture(s) for the pixel history depth/stencil values matching the capture's
|
|
// frambebuffer's formats
|
|
if(curStencil == curDepth)
|
|
{
|
|
driver->glGenTextures(1, ©Framebuffer.dsTextureId);
|
|
driver->CreateTextureImage(copyFramebuffer.dsTextureId, depthFormat, eGL_NONE, eGL_NONE,
|
|
textureTarget, 2, numEvents, 1, 1, numSamples, 1);
|
|
driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_DEPTH_STENCIL_ATTACHMENT,
|
|
copyFramebuffer.dsTextureId, 0);
|
|
|
|
if(multisampled)
|
|
{
|
|
driver->glGenTextures(1, ©Framebuffer.stencilViewId);
|
|
driver->glTextureView(copyFramebuffer.stencilViewId, textureTarget,
|
|
copyFramebuffer.dsTextureId, depthFormat, 0, 1, 0, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(curDepth != 0)
|
|
{
|
|
driver->glGenTextures(1, ©Framebuffer.depthTextureId);
|
|
driver->CreateTextureImage(copyFramebuffer.depthTextureId, depthFormat, eGL_NONE, eGL_NONE,
|
|
textureTarget, 2, numEvents, 1, 1, numSamples, 1);
|
|
driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
|
|
copyFramebuffer.depthTextureId, 0);
|
|
}
|
|
if(curStencil != 0)
|
|
{
|
|
driver->glGenTextures(1, ©Framebuffer.stencilTextureId);
|
|
driver->CreateTextureImage(copyFramebuffer.stencilTextureId, stencilFormat, eGL_NONE,
|
|
eGL_NONE, textureTarget, 2, numEvents, 1, 1, numSamples, 1);
|
|
driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
|
|
copyFramebuffer.stencilTextureId, 0);
|
|
}
|
|
}
|
|
|
|
// restore the capture's framebuffer
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer);
|
|
|
|
FramebufferKey key = {depthFormat, stencilFormat, numSamples};
|
|
copyFramebuffers[key] = copyFramebuffer;
|
|
return copyFramebuffers[key];
|
|
}
|
|
|
|
const CopyFramebuffer &getCopyFramebuffer(WrappedOpenGL *driver,
|
|
std::map<FramebufferKey, CopyFramebuffer> ©Framebuffers,
|
|
uint32_t numSamples, uint32_t numEvents)
|
|
{
|
|
GLuint curDepth;
|
|
GLint depthType;
|
|
GLuint curStencil;
|
|
GLint stencilType;
|
|
|
|
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
|
|
(GLint *)&curDepth);
|
|
|
|
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthType);
|
|
|
|
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
|
|
(GLint *)&curStencil);
|
|
|
|
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &stencilType);
|
|
|
|
GLenum depthFormat = eGL_NONE;
|
|
|
|
if(curDepth != 0)
|
|
{
|
|
ResourceId id;
|
|
if(depthType != eGL_RENDERBUFFER)
|
|
{
|
|
id = driver->GetResourceManager()->GetResID(TextureRes(driver->GetCtx(), curDepth));
|
|
}
|
|
else
|
|
{
|
|
id = driver->GetResourceManager()->GetResID(RenderbufferRes(driver->GetCtx(), curDepth));
|
|
}
|
|
depthFormat = driver->m_Textures[id].internalFormat;
|
|
}
|
|
|
|
GLenum stencilFormat = eGL_NONE;
|
|
if(curStencil != 0)
|
|
{
|
|
ResourceId id;
|
|
if(stencilType != eGL_RENDERBUFFER)
|
|
{
|
|
id = driver->GetResourceManager()->GetResID(TextureRes(driver->GetCtx(), curStencil));
|
|
}
|
|
else
|
|
{
|
|
id = driver->GetResourceManager()->GetResID(RenderbufferRes(driver->GetCtx(), curStencil));
|
|
}
|
|
stencilFormat = driver->m_Textures[id].internalFormat;
|
|
}
|
|
|
|
return getCopyFramebuffer(driver, copyFramebuffers, numSamples, numEvents, depthFormat,
|
|
stencilFormat);
|
|
}
|
|
|
|
GLbitfield getFramebufferCopyMask(WrappedOpenGL *driver)
|
|
{
|
|
GLuint curDepth = 0;
|
|
GLuint curStencil = 0;
|
|
GLuint curColor = 0;
|
|
|
|
GLint colorAttachment;
|
|
driver->glGetIntegerv(eGL_READ_BUFFER, &colorAttachment);
|
|
|
|
if(colorAttachment)
|
|
{
|
|
driver->glGetFramebufferAttachmentParameteriv(eGL_READ_FRAMEBUFFER, RDCGLenum(colorAttachment),
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
|
|
(GLint *)&curColor);
|
|
}
|
|
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
|
|
(GLint *)&curDepth);
|
|
|
|
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
|
|
(GLint *)&curStencil);
|
|
|
|
GLbitfield mask = 0;
|
|
if(curColor)
|
|
{
|
|
mask |= eGL_COLOR_BUFFER_BIT;
|
|
}
|
|
if(curDepth)
|
|
{
|
|
mask |= eGL_DEPTH_BUFFER_BIT;
|
|
}
|
|
if(curStencil)
|
|
{
|
|
mask |= eGL_STENCIL_BUFFER_BIT;
|
|
}
|
|
return mask;
|
|
}
|
|
|
|
bool PixelHistorySetupResources(WrappedOpenGL *driver, GLPixelHistoryResources &resources,
|
|
const TextureDescription &desc, const Subresource &sub,
|
|
uint32_t numEvents, GLuint glslVersion, uint32_t numSamples)
|
|
{
|
|
// Allocate a framebuffer that will render to the textures
|
|
driver->glGenFramebuffers(1, &resources.fullPrecisionFrameBuffer);
|
|
driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
|
|
|
|
// Allocate a texture for the pixel history colour values
|
|
bool multisampled = numSamples > 1;
|
|
GLenum textureTarget = multisampled ? eGL_TEXTURE_2D_MULTISAMPLE : eGL_TEXTURE_2D;
|
|
driver->glGenTextures(1, &resources.fullPrecisionColorImage);
|
|
driver->glBindTexture(textureTarget, resources.fullPrecisionColorImage);
|
|
driver->CreateTextureImage(resources.fullPrecisionColorImage, eGL_RGBA32F, eGL_NONE, eGL_NONE,
|
|
textureTarget, 2, desc.width >> sub.mip, desc.height >> sub.mip, 1,
|
|
numSamples, 1);
|
|
driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0,
|
|
resources.fullPrecisionColorImage, 0);
|
|
|
|
// Allocate a texture for the pixel history depth/stencil values
|
|
driver->glGenTextures(1, &resources.fullPrecisionDsImage);
|
|
driver->glBindTexture(textureTarget, resources.fullPrecisionDsImage);
|
|
driver->CreateTextureImage(resources.fullPrecisionDsImage, eGL_DEPTH24_STENCIL8, eGL_NONE,
|
|
eGL_NONE, textureTarget, 2, desc.width >> sub.mip,
|
|
desc.height >> sub.mip, 1, numSamples, 1);
|
|
driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_DEPTH_STENCIL_ATTACHMENT,
|
|
resources.fullPrecisionDsImage, 0);
|
|
|
|
// We will blit the color values to this texture and then use a compute shader to get the values
|
|
// for the sample that we want
|
|
driver->glGenBuffers(1, &resources.msCopyDstBuffer);
|
|
driver->glBindBuffer(eGL_SHADER_STORAGE_BUFFER, resources.msCopyDstBuffer);
|
|
driver->glNamedBufferDataEXT(
|
|
resources.msCopyDstBuffer,
|
|
8 * (sizeof(float)) * numEvents, // 8 floats per event (r,g,b,a,depth,null,null,null)
|
|
NULL, eGL_DYNAMIC_READ);
|
|
|
|
driver->glGenBuffers(1, &resources.msCopyUniformBlockBuffer);
|
|
driver->glBindBuffer(eGL_UNIFORM_BUFFER, resources.msCopyUniformBlockBuffer);
|
|
driver->glNamedBufferDataEXT(resources.msCopyUniformBlockBuffer, 8 * sizeof(uint32_t), NULL,
|
|
eGL_DYNAMIC_DRAW);
|
|
|
|
// If the GLSL version is greater than or equal to 330, we can use IntBitsToFloat, otherwise we
|
|
// need to write the float value directly.
|
|
rdcstr glslSource;
|
|
if(glslVersion >= 330)
|
|
{
|
|
glslSource = GenerateGLSLShader(GetEmbeddedResource(glsl_pixelhistory_primid_frag),
|
|
ShaderType::GLSL, glslVersion);
|
|
}
|
|
else
|
|
{
|
|
glslSource =
|
|
GenerateGLSLShader(GetEmbeddedResource(glsl_pixelhistory_primid_frag), ShaderType::GLSL,
|
|
glslVersion, "#define INT_BITS_TO_FLOAT_NOT_SUPPORTED\n");
|
|
}
|
|
// SPIR-V shaders are always generated as desktop GL 430, for ease
|
|
rdcstr spirvSource = GenerateGLSLShader(GetEmbeddedResource(glsl_pixelhistory_primid_frag),
|
|
ShaderType::GLSPIRV, 430);
|
|
rdcstr msCopySource =
|
|
GenerateGLSLShader(GetEmbeddedResource(glsl_pixelhistory_mscopy_comp), ShaderType::GLSL, 430);
|
|
rdcstr msCopySourceDepth = GenerateGLSLShader(
|
|
GetEmbeddedResource(glsl_pixelhistory_mscopy_depth_comp), ShaderType::GLSL, 430);
|
|
resources.primitiveIdFragmentShaderSPIRV = CreateSPIRVShader(eGL_FRAGMENT_SHADER, spirvSource);
|
|
resources.primitiveIdFragmentShader = CreateShader(eGL_FRAGMENT_SHADER, glslSource);
|
|
resources.msCopyComputeProgram = CreateCShaderProgram(msCopySource);
|
|
resources.msCopyDepthComputeProgram = CreateCShaderProgram(msCopySourceDepth);
|
|
return true;
|
|
}
|
|
|
|
bool PixelHistoryDestroyResources(WrappedOpenGL *driver, const GLPixelHistoryResources &resources)
|
|
{
|
|
driver->glDeleteTextures(1, &resources.fullPrecisionColorImage);
|
|
driver->glDeleteTextures(1, &resources.fullPrecisionDsImage);
|
|
driver->glDeleteFramebuffers(1, &resources.fullPrecisionFrameBuffer);
|
|
driver->glDeleteShader(resources.primitiveIdFragmentShader);
|
|
driver->glDeleteShader(resources.primitiveIdFragmentShaderSPIRV);
|
|
driver->glDeleteProgram(resources.msCopyComputeProgram);
|
|
driver->glDeleteProgram(resources.msCopyDepthComputeProgram);
|
|
driver->glDeleteBuffers(1, &resources.msCopyDstBuffer);
|
|
driver->glDeleteBuffers(1, &resources.msCopyUniformBlockBuffer);
|
|
|
|
for(const std::pair<const GLuint, GLuint> &resourceProgram : resources.programs)
|
|
{
|
|
driver->glDeleteProgram(resourceProgram.second);
|
|
}
|
|
|
|
for(const auto &pair : resources.copyFramebuffers)
|
|
{
|
|
const CopyFramebuffer &cf = pair.second;
|
|
driver->glDeleteFramebuffers(1, &cf.framebufferId);
|
|
driver->glDeleteTextures(1, &cf.colorTextureId);
|
|
driver->glDeleteTextures(1, &cf.dsTextureId);
|
|
driver->glDeleteTextures(1, &cf.depthTextureId);
|
|
driver->glDeleteTextures(1, &cf.stencilTextureId);
|
|
driver->glDeleteTextures(1, &cf.stencilViewId);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CopyMSSample(WrappedOpenGL *driver, const GLPixelHistoryResources &resources,
|
|
const CopyFramebuffer ©Framebuffer, int sampleIdx, int x, int y,
|
|
float *pixelDst)
|
|
{
|
|
GLint savedProgram;
|
|
driver->glGetIntegerv(eGL_CURRENT_PROGRAM, &savedProgram);
|
|
GLint savedActiveTexture;
|
|
driver->glGetIntegerv(eGL_ACTIVE_TEXTURE, &savedActiveTexture);
|
|
GLint savedShaderStorageBuffer;
|
|
driver->glGetIntegerv(eGL_SHADER_STORAGE_BUFFER_BINDING, &savedShaderStorageBuffer);
|
|
GLint savedUniformBuffer;
|
|
driver->glGetIntegerv(eGL_UNIFORM_BUFFER_BINDING, &savedUniformBuffer);
|
|
|
|
driver->glUseProgram(resources.msCopyComputeProgram);
|
|
GLint srcMSLoc = driver->glGetUniformLocation(resources.msCopyComputeProgram, "srcMS");
|
|
driver->glUniform1i(srcMSLoc, 0);
|
|
|
|
uint32_t uniforms[4] = {uint32_t(sampleIdx), uint32_t(x), uint32_t(y),
|
|
0}; // { sampleIdx, x, y, dstOffset }
|
|
driver->glBindBuffer(eGL_UNIFORM_BUFFER, resources.msCopyUniformBlockBuffer);
|
|
driver->glNamedBufferSubDataEXT(resources.msCopyUniformBlockBuffer, 0, sizeof(uniforms), uniforms);
|
|
|
|
driver->glBindBufferBase(eGL_UNIFORM_BUFFER, 3, resources.msCopyUniformBlockBuffer);
|
|
|
|
driver->glActiveTexture(eGL_TEXTURE0);
|
|
GLint savedMSTexture0;
|
|
driver->glGetIntegerv(eGL_TEXTURE_BINDING_2D_MULTISAMPLE, &savedMSTexture0);
|
|
driver->glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE, copyFramebuffer.colorTextureId);
|
|
|
|
driver->glBindBuffer(eGL_SHADER_STORAGE_BUFFER, resources.msCopyDstBuffer);
|
|
driver->glBindBufferBase(eGL_SHADER_STORAGE_BUFFER, 2, resources.msCopyDstBuffer);
|
|
|
|
driver->glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT);
|
|
driver->glDispatchCompute(1, 1, 1);
|
|
|
|
driver->glUseProgram(resources.msCopyDepthComputeProgram);
|
|
GLint depthMSLoc = driver->glGetUniformLocation(resources.msCopyDepthComputeProgram, "depthMS");
|
|
GLint stencilMSLoc =
|
|
driver->glGetUniformLocation(resources.msCopyDepthComputeProgram, "stencilMS");
|
|
|
|
driver->glUniform1i(depthMSLoc, 0);
|
|
driver->glUniform1i(stencilMSLoc, 1);
|
|
|
|
uint32_t newUniforms[6] = {uint32_t(sampleIdx), uint32_t(x), uint32_t(y), 1,
|
|
copyFramebuffer.dsTextureId != 0 || copyFramebuffer.depthTextureId != 0,
|
|
copyFramebuffer.dsTextureId != 0 ||
|
|
copyFramebuffer.stencilTextureId !=
|
|
0}; // { sampleIdx, x, y, dstOffset, hasDepth, hasStencil }
|
|
driver->glNamedBufferSubDataEXT(resources.msCopyUniformBlockBuffer, 0, sizeof(newUniforms),
|
|
newUniforms);
|
|
|
|
driver->glActiveTexture(eGL_TEXTURE1);
|
|
GLint savedMSTexture1;
|
|
driver->glGetIntegerv(eGL_TEXTURE_BINDING_2D_MULTISAMPLE, &savedMSTexture1);
|
|
|
|
if(copyFramebuffer.dsTextureId)
|
|
{
|
|
driver->glActiveTexture(eGL_TEXTURE0);
|
|
driver->glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE, copyFramebuffer.dsTextureId);
|
|
driver->glTexParameteri(eGL_TEXTURE_2D_MULTISAMPLE, eGL_DEPTH_STENCIL_TEXTURE_MODE,
|
|
eGL_DEPTH_COMPONENT);
|
|
|
|
driver->glActiveTexture(eGL_TEXTURE1);
|
|
driver->glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE, copyFramebuffer.stencilViewId);
|
|
driver->glTexParameteri(eGL_TEXTURE_2D_MULTISAMPLE, eGL_DEPTH_STENCIL_TEXTURE_MODE,
|
|
eGL_STENCIL_INDEX);
|
|
}
|
|
else
|
|
{
|
|
driver->glActiveTexture(eGL_TEXTURE0);
|
|
driver->glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE, copyFramebuffer.depthTextureId);
|
|
|
|
driver->glActiveTexture(eGL_TEXTURE1);
|
|
driver->glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE, copyFramebuffer.stencilTextureId);
|
|
}
|
|
|
|
driver->glDispatchCompute(1, 1, 1);
|
|
driver->glMemoryBarrier(eGL_SHADER_STORAGE_BARRIER_BIT);
|
|
|
|
driver->glGetBufferSubData(eGL_SHADER_STORAGE_BUFFER, 0, 8 * 4, pixelDst);
|
|
|
|
driver->glUseProgram(savedProgram);
|
|
driver->glActiveTexture(eGL_TEXTURE0);
|
|
driver->glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE, savedMSTexture0);
|
|
driver->glActiveTexture(eGL_TEXTURE1);
|
|
driver->glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE, savedMSTexture1);
|
|
driver->glActiveTexture((RDCGLenum)savedActiveTexture);
|
|
driver->glBindBuffer(eGL_SHADER_STORAGE_BUFFER, savedShaderStorageBuffer);
|
|
driver->glBindBuffer(eGL_UNIFORM_BUFFER, savedShaderStorageBuffer);
|
|
}
|
|
|
|
rdcarray<EventUsage> QueryModifyingEvents(WrappedOpenGL *driver, GLPixelHistoryResources &resources,
|
|
const rdcarray<EventUsage> &events, int x, int y,
|
|
rdcarray<PixelModification> &history)
|
|
{
|
|
rdcarray<EventUsage> modEvents;
|
|
rdcarray<GLuint> occlusionQueries;
|
|
occlusionQueries.resize(events.size());
|
|
driver->glGenQueries((GLsizei)occlusionQueries.size(), occlusionQueries.data());
|
|
|
|
driver->ReplayLog(0, events[0].eventId, eReplay_WithoutDraw);
|
|
// execute the occlusion queries
|
|
for(size_t i = 0; i < events.size(); i++)
|
|
{
|
|
if(!(events[i].usage == ResourceUsage::Clear || isDirectWrite(events[i].usage)))
|
|
{
|
|
driver->glDisable(eGL_DEPTH_TEST);
|
|
driver->glDisable(eGL_STENCIL_TEST);
|
|
driver->glDisable(eGL_CULL_FACE);
|
|
driver->glDisable(eGL_SAMPLE_MASK);
|
|
driver->glDisable(eGL_DEPTH_CLAMP);
|
|
driver->glEnable(eGL_SCISSOR_TEST);
|
|
driver->glScissor(x, y, 1, 1);
|
|
|
|
driver->SetFetchCounters(true);
|
|
driver->glBeginQuery(eGL_SAMPLES_PASSED, occlusionQueries[i]);
|
|
driver->ReplayLog(events[i].eventId, events[i].eventId, eReplay_OnlyDraw);
|
|
driver->glEndQuery(eGL_SAMPLES_PASSED);
|
|
driver->SetFetchCounters(false);
|
|
}
|
|
|
|
if(i < events.size() - 1)
|
|
{
|
|
driver->ReplayLog(events[i].eventId + 1, events[i + 1].eventId, eReplay_WithoutDraw);
|
|
}
|
|
}
|
|
// read back the occlusion queries and generate the list of potentially modifying events
|
|
for(size_t i = 0; i < events.size(); i++)
|
|
{
|
|
if(events[i].usage == ResourceUsage::Clear || isDirectWrite(events[i].usage))
|
|
{
|
|
PixelModification mod;
|
|
RDCEraseEl(mod);
|
|
mod.eventId = events[i].eventId;
|
|
history.push_back(mod);
|
|
|
|
modEvents.push_back(events[i]);
|
|
}
|
|
else
|
|
{
|
|
uint32_t numSamples;
|
|
driver->glGetQueryObjectuiv(occlusionQueries[i], eGL_QUERY_RESULT, &numSamples);
|
|
|
|
if(numSamples > 0)
|
|
{
|
|
PixelModification mod;
|
|
RDCEraseEl(mod);
|
|
mod.eventId = events[i].eventId;
|
|
history.push_back(mod);
|
|
modEvents.push_back(events[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
driver->glDeleteQueries((GLsizei)occlusionQueries.size(), occlusionQueries.data());
|
|
return modEvents;
|
|
}
|
|
|
|
void readPixelValuesMS(WrappedOpenGL *driver, const GLPixelHistoryResources &resources,
|
|
const CopyFramebuffer ©Framebuffer, int sampleIdx, int x, int y,
|
|
rdcarray<PixelModification> &history, int historyIndex, bool readStencil)
|
|
{
|
|
rdcarray<float> pixelValue;
|
|
pixelValue.resize(8);
|
|
CopyMSSample(driver, resources, copyFramebuffer, sampleIdx, x, y, pixelValue.data());
|
|
|
|
const int depthOffset = 4;
|
|
const int stencilOffset = 5;
|
|
ModificationValue &modValue = history[historyIndex].postMod;
|
|
|
|
for(int j = 0; j < 4; ++j)
|
|
{
|
|
modValue.col.floatValue[j] = pixelValue[j];
|
|
}
|
|
modValue.depth = pixelValue[depthOffset];
|
|
if(readStencil)
|
|
{
|
|
modValue.stencil = *(int *)&pixelValue[stencilOffset];
|
|
}
|
|
}
|
|
|
|
void QueryPostModPixelValues(WrappedOpenGL *driver, GLPixelHistoryResources &resources,
|
|
const rdcarray<EventUsage> &modEvents, int x, int y,
|
|
rdcarray<PixelModification> &history, uint32_t numSamples,
|
|
uint32_t sampleIndex)
|
|
{
|
|
driver->ReplayLog(0, modEvents[0].eventId, eReplay_WithoutDraw);
|
|
CopyFramebuffer copyFramebuffer;
|
|
RDCEraseEl(copyFramebuffer);
|
|
|
|
for(size_t i = 0; i < modEvents.size(); i++)
|
|
{
|
|
driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_OnlyDraw);
|
|
|
|
GLint savedReadFramebuffer, savedDrawFramebuffer;
|
|
driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer);
|
|
driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer);
|
|
|
|
if(numSamples > 1)
|
|
{
|
|
copyFramebuffer =
|
|
getCopyFramebuffer(driver, resources.copyFramebuffers, numSamples, int(modEvents.size()));
|
|
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, copyFramebuffer.framebufferId);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedDrawFramebuffer);
|
|
|
|
SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver),
|
|
eGL_NEAREST);
|
|
readPixelValuesMS(driver, resources, copyFramebuffer, sampleIndex, 0, 0, history, int(i), true);
|
|
}
|
|
else
|
|
{
|
|
copyFramebuffer = getCopyFramebuffer(driver, resources.copyFramebuffers, 1 /*single sampled*/,
|
|
int(modEvents.size()));
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, copyFramebuffer.framebufferId);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedDrawFramebuffer);
|
|
|
|
SafeBlitFramebuffer(x, y, x + 1, y + 1, GLint(i), 0, GLint(i) + 1, 1,
|
|
getFramebufferCopyMask(driver), eGL_NEAREST);
|
|
}
|
|
|
|
// restore the capture's framebuffer
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer);
|
|
|
|
if(i < modEvents.size() - 1)
|
|
{
|
|
driver->ReplayLog(modEvents[i].eventId + 1, modEvents[i + 1].eventId, eReplay_WithoutDraw);
|
|
}
|
|
}
|
|
|
|
if(numSamples == 1)
|
|
{
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, copyFramebuffer.framebufferId);
|
|
rdcarray<float> colourValues;
|
|
colourValues.resize(4 * modEvents.size());
|
|
rdcarray<float> depthValues;
|
|
depthValues.resize(modEvents.size());
|
|
rdcarray<int> stencilValues;
|
|
stencilValues.resize(modEvents.size());
|
|
driver->glReadPixels(0, 0, GLint(modEvents.size()), 1, eGL_RGBA, eGL_FLOAT,
|
|
(void *)colourValues.data());
|
|
if(copyFramebuffer.dsTextureId != 0 || copyFramebuffer.depthTextureId != 0)
|
|
{
|
|
driver->glReadPixels(0, 0, GLint(modEvents.size()), 1, eGL_DEPTH_COMPONENT, eGL_FLOAT,
|
|
(void *)depthValues.data());
|
|
}
|
|
|
|
if(copyFramebuffer.dsTextureId != 0 || copyFramebuffer.stencilTextureId != 0)
|
|
{
|
|
driver->glReadPixels(0, 0, GLint(modEvents.size()), 1, eGL_STENCIL_INDEX, eGL_INT,
|
|
(void *)stencilValues.data());
|
|
}
|
|
|
|
for(size_t i = 0; i < modEvents.size(); i++)
|
|
{
|
|
ModificationValue modValue;
|
|
|
|
for(int j = 0; j < 4; ++j)
|
|
{
|
|
modValue.col.floatValue[j] = colourValues[i * 4 + j];
|
|
}
|
|
modValue.depth = depthValues[i];
|
|
modValue.stencil = stencilValues[i];
|
|
|
|
history[i].postMod = modValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void readShaderOutMS(WrappedOpenGL *driver, const GLPixelHistoryResources &resources,
|
|
const CopyFramebuffer ©Framebuffer, int sampleIdx, int x, int y,
|
|
rdcarray<PixelModification> &history, int historyIndex)
|
|
{
|
|
rdcarray<float> pixelValue;
|
|
pixelValue.resize(8);
|
|
CopyMSSample(driver, resources, copyFramebuffer, sampleIdx, x, y, pixelValue.data());
|
|
|
|
const int depthOffset = 4;
|
|
const int stencilOffset = 5;
|
|
ModificationValue modValue;
|
|
|
|
for(int j = 0; j < 4; ++j)
|
|
{
|
|
modValue.col.floatValue[j] = pixelValue[j];
|
|
}
|
|
modValue.depth = pixelValue[depthOffset];
|
|
modValue.stencil = *(int *)&pixelValue[stencilOffset];
|
|
history[historyIndex].shaderOut = modValue;
|
|
}
|
|
|
|
// This function a) calculates the number of fagments per event
|
|
// and b) calculates the shader output values per event
|
|
std::map<uint32_t, uint32_t> QueryNumFragmentsByEvent(WrappedOpenGL *driver,
|
|
GLPixelHistoryResources &resources,
|
|
const rdcarray<EventUsage> &modEvents,
|
|
rdcarray<PixelModification> &history, int x,
|
|
int y, uint32_t numSamples,
|
|
uint32_t sampleIndex)
|
|
{
|
|
driver->ReplayLog(0, modEvents[0].eventId, eReplay_WithoutDraw);
|
|
|
|
std::map<uint32_t, uint32_t> eventFragments;
|
|
|
|
for(size_t i = 0; i < modEvents.size(); ++i)
|
|
{
|
|
GLint savedReadFramebuffer, savedDrawFramebuffer;
|
|
driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer);
|
|
driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer);
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
|
|
|
|
driver->glStencilOp(eGL_INCR, eGL_INCR, eGL_INCR);
|
|
driver->glStencilMask(0xff); // default for 1 byte
|
|
driver->glStencilFunc(eGL_ALWAYS, 0, 0xff);
|
|
driver->glClearStencil(0);
|
|
driver->glClearColor(0, 0, 0, 0);
|
|
driver->glClearDepth(0);
|
|
driver->glClear(eGL_STENCIL_BUFFER_BIT | eGL_COLOR_BUFFER_BIT | eGL_DEPTH_BUFFER_BIT);
|
|
driver->glEnable(eGL_STENCIL_TEST);
|
|
// depth test enable
|
|
driver->glEnable(eGL_DEPTH_TEST);
|
|
driver->glDepthFunc(eGL_ALWAYS);
|
|
driver->glDepthMask(GL_TRUE);
|
|
driver->glDisable(eGL_BLEND);
|
|
|
|
// enable the sample we're looking at so we get the shaderOut even if it's masked off
|
|
driver->glEnable(eGL_SAMPLE_MASK);
|
|
|
|
driver->glSampleMaski(0, 1u << sampleIndex);
|
|
|
|
// replay event
|
|
driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_OnlyDraw);
|
|
|
|
uint32_t numFragments = 0;
|
|
if(numSamples == 1)
|
|
{
|
|
ModificationValue modValue;
|
|
driver->glReadPixels(x, y, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)modValue.col.floatValue.data());
|
|
driver->glReadPixels(x, y, 1, 1, eGL_DEPTH_COMPONENT, eGL_FLOAT, (void *)&modValue.depth);
|
|
driver->glReadPixels(x, y, 1, 1, eGL_STENCIL_INDEX, eGL_UNSIGNED_INT, (void *)&numFragments);
|
|
|
|
// We're not reading the stencil value here, so use the postMod instead.
|
|
// Shaders don't actually output stencil values, those are determined by the stencil op.
|
|
modValue.stencil = history[i].postMod.stencil;
|
|
|
|
history[i].shaderOut = modValue;
|
|
}
|
|
else
|
|
{
|
|
const CopyFramebuffer ©Framebuffer =
|
|
getCopyFramebuffer(driver, resources.copyFramebuffers, numSamples, int(modEvents.size()),
|
|
eGL_DEPTH24_STENCIL8, eGL_DEPTH24_STENCIL8);
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, copyFramebuffer.framebufferId);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
|
|
SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver),
|
|
eGL_NEAREST);
|
|
readShaderOutMS(driver, resources, copyFramebuffer, sampleIndex, 0, 0, history, int(i));
|
|
numFragments = history[i].shaderOut.stencil;
|
|
history[i].shaderOut.stencil = history[i].postMod.stencil;
|
|
}
|
|
|
|
eventFragments.emplace(modEvents[i].eventId, numFragments);
|
|
|
|
// restore the capture's framebuffer
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer);
|
|
|
|
if(i < modEvents.size() - 1)
|
|
{
|
|
driver->ReplayLog(modEvents[i].eventId + 1, modEvents[i + 1].eventId, eReplay_WithoutDraw);
|
|
}
|
|
}
|
|
|
|
return eventFragments;
|
|
}
|
|
|
|
bool QueryScissorTest(WrappedOpenGL *driver, GLPixelHistoryResources &resources,
|
|
const EventUsage event, int x, int y)
|
|
{
|
|
driver->ReplayLog(0, event.eventId, eReplay_WithoutDraw);
|
|
bool scissorTestFailed = false;
|
|
if(driver->glIsEnabled(eGL_SCISSOR_TEST))
|
|
{
|
|
int scissorBox[4]; // [x, y, width, height]
|
|
driver->glGetIntegerv(eGL_SCISSOR_BOX, scissorBox);
|
|
scissorTestFailed = x < scissorBox[0] || x - scissorBox[0] >= scissorBox[2] ||
|
|
y < scissorBox[1] || y - scissorBox[1] >= scissorBox[3];
|
|
}
|
|
return scissorTestFailed;
|
|
}
|
|
|
|
bool QueryTest(WrappedOpenGL *driver, GLPixelHistoryResources &resources, const EventUsage event,
|
|
int x, int y, OpenGLTest test, uint32_t sampleIndex)
|
|
{
|
|
driver->ReplayLog(0, event.eventId - 1, eReplay_Full);
|
|
GLuint samplesPassedQuery;
|
|
driver->glGenQueries(1, &samplesPassedQuery);
|
|
driver->glEnable(eGL_SCISSOR_TEST);
|
|
driver->glScissor(x, y, 1, 1);
|
|
if(test < OpenGLTest::DepthTest)
|
|
{
|
|
driver->glDisable(eGL_DEPTH_TEST);
|
|
}
|
|
if(test < OpenGLTest::StencilTest)
|
|
{
|
|
driver->glDisable(eGL_STENCIL_TEST);
|
|
}
|
|
if(test < OpenGLTest::FaceCulling)
|
|
{
|
|
driver->glDisable(eGL_CULL_FACE);
|
|
}
|
|
if(test < OpenGLTest::SampleMask)
|
|
{
|
|
driver->glDisable(eGL_SAMPLE_MASK);
|
|
}
|
|
else if(test == OpenGLTest::SampleMask)
|
|
{
|
|
GLboolean sampleMaskEnabled = driver->glIsEnabled(eGL_SAMPLE_MASK);
|
|
if(sampleMaskEnabled)
|
|
{
|
|
uint32_t currentSampleMask;
|
|
driver->glGetIntegeri_v(eGL_SAMPLE_MASK_VALUE, 0, (GLint *)¤tSampleMask);
|
|
uint32_t newSampleMask = currentSampleMask & (sampleIndex == ~0u ? ~0u : (1u << sampleIndex));
|
|
driver->glSampleMaski(0, newSampleMask);
|
|
}
|
|
else
|
|
{
|
|
driver->glEnable(eGL_SAMPLE_MASK);
|
|
driver->glSampleMaski(0, 1u << sampleIndex);
|
|
}
|
|
}
|
|
|
|
driver->SetFetchCounters(true);
|
|
driver->glBeginQuery(eGL_SAMPLES_PASSED, samplesPassedQuery);
|
|
driver->ReplayLog(event.eventId, event.eventId, eReplay_Full);
|
|
driver->glEndQuery(eGL_SAMPLES_PASSED);
|
|
driver->SetFetchCounters(false);
|
|
int numSamplesPassed;
|
|
driver->glGetQueryObjectiv(samplesPassedQuery, eGL_QUERY_RESULT, &numSamplesPassed);
|
|
driver->glDeleteQueries(1, &samplesPassedQuery);
|
|
return numSamplesPassed == 0;
|
|
}
|
|
|
|
void QueryFailedTests(WrappedOpenGL *driver, GLPixelHistoryResources &resources,
|
|
const rdcarray<EventUsage> &modEvents, int x, int y,
|
|
rdcarray<PixelModification> &history, uint32_t sampleIndex)
|
|
{
|
|
for(size_t i = 0; i < modEvents.size(); ++i)
|
|
{
|
|
OpenGLTest failedTest = OpenGLTest::NumTests;
|
|
if(!isDirectWrite(modEvents[i].usage))
|
|
{
|
|
if(modEvents[i].usage == ResourceUsage::Clear)
|
|
{
|
|
bool failed = QueryScissorTest(driver, resources, modEvents[i], x, y);
|
|
if(failed)
|
|
{
|
|
failedTest = OpenGLTest::ScissorTest;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int test = 0; test < int(OpenGLTest::NumTests); ++test)
|
|
{
|
|
bool failed;
|
|
if(test == int(OpenGLTest::ScissorTest))
|
|
{
|
|
failed = QueryScissorTest(driver, resources, modEvents[i], x, y);
|
|
}
|
|
else
|
|
{
|
|
failed = QueryTest(driver, resources, modEvents[i], x, y, OpenGLTest(test), sampleIndex);
|
|
}
|
|
if(failed)
|
|
{
|
|
failedTest = OpenGLTest(test);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ensure that history objects are one-to-one mapped with modEvent objects
|
|
RDCASSERT(history[i].eventId == modEvents[i].eventId);
|
|
history[i].scissorClipped = failedTest == OpenGLTest::ScissorTest;
|
|
history[i].stencilTestFailed = failedTest == OpenGLTest::StencilTest;
|
|
history[i].depthTestFailed = failedTest == OpenGLTest::DepthTest;
|
|
history[i].backfaceCulled = failedTest == OpenGLTest::FaceCulling;
|
|
history[i].sampleMasked = failedTest == OpenGLTest::SampleMask;
|
|
}
|
|
}
|
|
|
|
void QueryShaderOutPerFragment(WrappedOpenGL *driver, GLReplay *replay,
|
|
GLPixelHistoryResources &resources,
|
|
const rdcarray<EventUsage> &modEvents, int x, int y,
|
|
rdcarray<PixelModification> &history,
|
|
const std::map<uint32_t, uint32_t> &eventFragments,
|
|
uint32_t numSamples, uint32_t sampleIndex)
|
|
{
|
|
driver->ReplayLog(0, modEvents[0].eventId, eReplay_WithoutDraw);
|
|
for(size_t i = 0; i < modEvents.size(); ++i)
|
|
{
|
|
auto it = eventFragments.find(modEvents[i].eventId);
|
|
uint32_t numFragments = (it != eventFragments.end()) ? it->second : 0;
|
|
|
|
if(numFragments <= 1)
|
|
{
|
|
if(i < modEvents.size() - 1)
|
|
{
|
|
driver->ReplayLog(modEvents[i].eventId, modEvents[i + 1].eventId, eReplay_WithoutDraw);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
driver->glEnable(eGL_SCISSOR_TEST);
|
|
driver->glScissor(x, y, 1, 1);
|
|
|
|
driver->glClearStencil(0);
|
|
driver->glClearColor(0, 0, 0, 0);
|
|
driver->glClearDepth(0);
|
|
driver->glClearStencil(0);
|
|
|
|
driver->glEnable(eGL_DEPTH_TEST);
|
|
driver->glDepthFunc(eGL_ALWAYS);
|
|
driver->glDepthMask(GL_TRUE);
|
|
driver->glDisable(eGL_BLEND);
|
|
driver->glEnable(eGL_SAMPLE_MASK);
|
|
if(sampleIndex != ~0u)
|
|
{
|
|
driver->glSampleMaski(0, 1u << sampleIndex);
|
|
}
|
|
else
|
|
{
|
|
driver->glSampleMaski(0, ~0u);
|
|
}
|
|
|
|
driver->glStencilMask(0xff);
|
|
driver->glStencilOp(eGL_INCR, eGL_INCR, eGL_INCR);
|
|
driver->glEnable(eGL_STENCIL_TEST);
|
|
|
|
// enable the sample we're looking at so we get the shaderOut even if it's masked off
|
|
if(sampleIndex != ~0u)
|
|
{
|
|
driver->glSampleMaski(0, 1u << sampleIndex);
|
|
}
|
|
|
|
PixelModification referenceHistory;
|
|
referenceHistory.eventId = modEvents[i].eventId;
|
|
|
|
auto historyIndex =
|
|
std::lower_bound(history.begin(), history.end(), referenceHistory,
|
|
[](const PixelModification &h1, const PixelModification &h2) -> bool {
|
|
return h1.eventId < h2.eventId;
|
|
});
|
|
|
|
RDCASSERT(historyIndex != history.end());
|
|
|
|
GLint savedReadFramebuffer, savedDrawFramebuffer;
|
|
driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer);
|
|
driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer);
|
|
|
|
for(size_t j = 0; j < RDCMAX(numFragments, 1u); ++j)
|
|
{
|
|
// Set the stencil function so only jth fragment will pass.
|
|
driver->glStencilFunc(eGL_EQUAL, (int)j, 0xff);
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
|
|
driver->glClear(eGL_STENCIL_BUFFER_BIT | eGL_COLOR_BUFFER_BIT | eGL_DEPTH_BUFFER_BIT);
|
|
|
|
driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_OnlyDraw);
|
|
|
|
if(numSamples == 1)
|
|
{
|
|
ModificationValue modValue;
|
|
driver->glReadPixels(x, y, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)modValue.col.floatValue.data());
|
|
driver->glReadPixels(x, y, 1, 1, eGL_DEPTH_COMPONENT, eGL_FLOAT, (void *)&modValue.depth);
|
|
modValue.stencil = historyIndex->shaderOut.stencil;
|
|
|
|
historyIndex->shaderOut = modValue;
|
|
}
|
|
else
|
|
{
|
|
const CopyFramebuffer ©Framebuffer =
|
|
getCopyFramebuffer(driver, resources.copyFramebuffers, numSamples,
|
|
int(modEvents.size()), eGL_DEPTH24_STENCIL8, eGL_DEPTH24_STENCIL8);
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, copyFramebuffer.framebufferId);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
|
|
SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver),
|
|
eGL_NEAREST);
|
|
int oldStencil = historyIndex->shaderOut.stencil;
|
|
readShaderOutMS(driver, resources, copyFramebuffer, sampleIndex, 0, 0, history,
|
|
int(historyIndex - history.begin()));
|
|
historyIndex->shaderOut.stencil = oldStencil;
|
|
}
|
|
historyIndex++;
|
|
}
|
|
|
|
// restore the capture's framebuffer
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer);
|
|
|
|
if(i < modEvents.size() - 1)
|
|
{
|
|
driver->ReplayLog(modEvents[i].eventId + 1, modEvents[i + 1].eventId, eReplay_WithoutDraw);
|
|
}
|
|
}
|
|
}
|
|
|
|
void QueryPostModPerFragment(WrappedOpenGL *driver, GLReplay *replay,
|
|
GLPixelHistoryResources &resources,
|
|
const rdcarray<EventUsage> &modEvents, int x, int y,
|
|
rdcarray<PixelModification> &history,
|
|
const std::map<uint32_t, uint32_t> &eventFragments,
|
|
uint32_t numSamples, uint32_t sampleIndex)
|
|
{
|
|
driver->ReplayLog(0, modEvents[0].eventId - 1, eReplay_WithoutDraw);
|
|
|
|
for(size_t i = 0; i < modEvents.size(); i++)
|
|
{
|
|
auto it = eventFragments.find(modEvents[i].eventId);
|
|
uint32_t numFragments = (it != eventFragments.end()) ? it->second : 0;
|
|
|
|
if(numFragments <= 1)
|
|
{
|
|
if(i < modEvents.size() - 1)
|
|
{
|
|
driver->ReplayLog(modEvents[i].eventId, modEvents[i + 1].eventId, eReplay_WithoutDraw);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
driver->glEnable(eGL_SCISSOR_TEST);
|
|
driver->glScissor(x, y, 1, 1);
|
|
|
|
driver->glClearStencil(0);
|
|
|
|
driver->glStencilMask(0xff);
|
|
driver->glStencilOp(eGL_INCR, eGL_INCR, eGL_INCR);
|
|
driver->glEnable(eGL_STENCIL_TEST);
|
|
|
|
PixelModification referenceHistory;
|
|
referenceHistory.eventId = modEvents[i].eventId;
|
|
|
|
auto historyIndex =
|
|
std::lower_bound(history.begin(), history.end(), referenceHistory,
|
|
[](const PixelModification &h1, const PixelModification &h2) -> bool {
|
|
return h1.eventId < h2.eventId;
|
|
});
|
|
RDCASSERT(historyIndex != history.end());
|
|
|
|
GLint savedReadFramebuffer, savedDrawFramebuffer;
|
|
driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer);
|
|
driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer);
|
|
|
|
CopyFramebuffer copyFramebuffer;
|
|
RDCEraseEl(copyFramebuffer);
|
|
|
|
for(size_t j = 0; j < std::max(numFragments, 1u); ++j)
|
|
{
|
|
// Set the stencil function so only jth fragment will pass.
|
|
driver->glStencilFunc(eGL_EQUAL, (int)j, 0xff);
|
|
driver->glClear(eGL_STENCIL_BUFFER_BIT);
|
|
|
|
driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_OnlyDraw);
|
|
|
|
if(numSamples > 1)
|
|
{
|
|
copyFramebuffer = getCopyFramebuffer(driver, resources.copyFramebuffers, numSamples,
|
|
int(modEvents.size()));
|
|
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, copyFramebuffer.framebufferId);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedDrawFramebuffer);
|
|
SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver),
|
|
eGL_NEAREST);
|
|
|
|
readPixelValuesMS(driver, resources, copyFramebuffer, sampleIndex, 0, 0, history,
|
|
int(historyIndex - history.begin()), false);
|
|
historyIndex++;
|
|
}
|
|
else
|
|
{
|
|
// Blit the values into out framebuffer
|
|
copyFramebuffer = getCopyFramebuffer(driver, resources.copyFramebuffers,
|
|
1 /*single sampled*/, int(modEvents.size()));
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, copyFramebuffer.framebufferId);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedDrawFramebuffer);
|
|
SafeBlitFramebuffer(x, y, x + 1, y + 1, GLint(j), 0, GLint(j) + 1, 1,
|
|
getFramebufferCopyMask(driver), eGL_NEAREST);
|
|
}
|
|
|
|
// restore the capture's framebuffer
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer);
|
|
}
|
|
|
|
if(numSamples == 1)
|
|
{
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, copyFramebuffer.framebufferId);
|
|
rdcarray<float> colourValues;
|
|
colourValues.resize(4 * numFragments);
|
|
rdcarray<float> depthValues;
|
|
depthValues.resize(numFragments);
|
|
driver->glReadPixels(0, 0, numFragments, 1, eGL_RGBA, eGL_FLOAT, (void *)colourValues.data());
|
|
driver->glReadPixels(0, 0, numFragments, 1, eGL_DEPTH_COMPONENT, eGL_FLOAT,
|
|
(void *)depthValues.data());
|
|
for(size_t j = 0; j < numFragments; j++)
|
|
{
|
|
ModificationValue modValue;
|
|
|
|
for(int k = 0; k < 4; ++k)
|
|
{
|
|
modValue.col.floatValue[k] = colourValues[j * 4 + k];
|
|
}
|
|
modValue.depth = depthValues[j];
|
|
modValue.stencil = historyIndex->postMod.stencil;
|
|
|
|
historyIndex->postMod = modValue;
|
|
historyIndex++;
|
|
}
|
|
}
|
|
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer);
|
|
if(i < modEvents.size() - 1)
|
|
{
|
|
driver->ReplayLog(modEvents[i].eventId + 1, modEvents[i + 1].eventId, eReplay_WithoutDraw);
|
|
}
|
|
}
|
|
}
|
|
|
|
void QueryPrimitiveIdPerFragment(WrappedOpenGL *driver, GLReplay *replay,
|
|
GLPixelHistoryResources &resources,
|
|
const rdcarray<EventUsage> &modEvents, int x, int y,
|
|
rdcarray<PixelModification> &history,
|
|
const std::map<uint32_t, uint32_t> &eventFragments,
|
|
bool usingFloatForPrimitiveId, uint32_t numSamples,
|
|
uint32_t sampleIndex)
|
|
{
|
|
driver->ReplayLog(0, modEvents[0].eventId - 1, eReplay_WithoutDraw);
|
|
|
|
for(size_t i = 0; i < modEvents.size(); i++)
|
|
{
|
|
auto it = eventFragments.find(modEvents[i].eventId);
|
|
uint32_t numFragments = (it != eventFragments.end()) ? it->second : 0;
|
|
|
|
if(numFragments == 0)
|
|
{
|
|
if(i < modEvents.size() - 1)
|
|
{
|
|
driver->ReplayLog(modEvents[i].eventId, modEvents[i + 1].eventId, eReplay_WithoutDraw);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
|
|
driver->glReadBuffer(eGL_COLOR_ATTACHMENT0);
|
|
|
|
driver->glEnable(eGL_SCISSOR_TEST);
|
|
driver->glScissor(x, y, 1, 1);
|
|
|
|
driver->glClearStencil(0);
|
|
driver->glEnable(eGL_DEPTH_TEST);
|
|
driver->glDepthFunc(eGL_ALWAYS);
|
|
driver->glDepthMask(GL_TRUE);
|
|
driver->glDisable(eGL_BLEND);
|
|
driver->glDisable(eGL_SAMPLE_MASK);
|
|
|
|
driver->glStencilMask(0xff);
|
|
driver->glStencilOp(eGL_INCR, eGL_INCR, eGL_INCR);
|
|
driver->glEnable(eGL_STENCIL_TEST);
|
|
|
|
PixelModification referenceHistory;
|
|
referenceHistory.eventId = modEvents[i].eventId;
|
|
|
|
auto historyIndex =
|
|
std::lower_bound(history.begin(), history.end(), referenceHistory,
|
|
[](const PixelModification &h1, const PixelModification &h2) -> bool {
|
|
return h1.eventId < h2.eventId;
|
|
});
|
|
RDCASSERT(historyIndex != history.end());
|
|
|
|
GLint currentProgram = 0;
|
|
|
|
// we expect this value to be overwritten by the primitive id shader. if not
|
|
// it will cause an assertion that all color values are the same to fail
|
|
driver->glClearColor(0.84f, 0.17f, 0.2f, 0.49f);
|
|
driver->glClear(eGL_COLOR_BUFFER_BIT);
|
|
|
|
driver->glGetIntegerv(eGL_CURRENT_PROGRAM, ¤tProgram);
|
|
|
|
driver->glUseProgram(GetPrimitiveIdProgram(driver, replay, resources, currentProgram));
|
|
|
|
for(size_t j = 0; j < std::max(numFragments, 1u); ++j)
|
|
{
|
|
ModificationValue modValue;
|
|
modValue.stencil = 0;
|
|
// Set the stencil function so only jth fragment will pass.
|
|
driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
|
|
driver->glStencilFunc(eGL_EQUAL, (int)j, 0xff);
|
|
driver->glClear(eGL_STENCIL_BUFFER_BIT);
|
|
|
|
driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_OnlyDraw);
|
|
|
|
float primitiveIds[8];
|
|
if(numSamples == 1)
|
|
{
|
|
driver->glReadPixels(x, y, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)primitiveIds);
|
|
}
|
|
else
|
|
{
|
|
const CopyFramebuffer ©Framebuffer =
|
|
getCopyFramebuffer(driver, resources.copyFramebuffers, numSamples,
|
|
int(modEvents.size()), eGL_DEPTH24_STENCIL8, eGL_DEPTH24_STENCIL8);
|
|
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, copyFramebuffer.framebufferId);
|
|
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
|
|
SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver),
|
|
eGL_NEAREST);
|
|
CopyMSSample(driver, resources, copyFramebuffer, sampleIndex, 0, 0, primitiveIds);
|
|
}
|
|
RDCASSERT(primitiveIds[0] == primitiveIds[1] && primitiveIds[0] == primitiveIds[2] &&
|
|
primitiveIds[0] == primitiveIds[3]);
|
|
if(usingFloatForPrimitiveId)
|
|
{
|
|
historyIndex->primitiveID = int(primitiveIds[0]);
|
|
}
|
|
else
|
|
{
|
|
historyIndex->primitiveID = *(int *)primitiveIds;
|
|
}
|
|
|
|
++historyIndex;
|
|
}
|
|
driver->glUseProgram(currentProgram);
|
|
|
|
if(i < modEvents.size() - 1)
|
|
{
|
|
driver->ReplayLog(modEvents[i].eventId + 1, modEvents[i + 1].eventId, eReplay_WithoutDraw);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool depthTestPassed(int depthFunc, float shaderOutputDepth, float depthInBuffer)
|
|
{
|
|
switch(depthFunc)
|
|
{
|
|
case eGL_NEVER: return false;
|
|
case eGL_LESS: return shaderOutputDepth < depthInBuffer;
|
|
case eGL_EQUAL: return shaderOutputDepth == depthInBuffer;
|
|
case eGL_LEQUAL: return shaderOutputDepth <= depthInBuffer;
|
|
case eGL_GREATER: return shaderOutputDepth > depthInBuffer;
|
|
case eGL_NOTEQUAL: return shaderOutputDepth != depthInBuffer;
|
|
case eGL_GEQUAL: return shaderOutputDepth >= depthInBuffer;
|
|
case eGL_ALWAYS: return true;
|
|
|
|
default: RDCERR("Unexpected depth function: %d", depthFunc);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CalculateFragmentDepthTests(WrappedOpenGL *driver, GLPixelHistoryResources &resources,
|
|
const rdcarray<EventUsage> &modEvents,
|
|
rdcarray<PixelModification> &history,
|
|
const std::map<uint32_t, uint32_t> &eventFragments)
|
|
{
|
|
driver->ReplayLog(0, modEvents[0].eventId, eReplay_WithoutDraw);
|
|
size_t historyIndex = 0;
|
|
for(size_t i = 0; i < modEvents.size(); ++i)
|
|
{
|
|
// We only need to calculate the depth test for events with multiple fragments.
|
|
auto it = eventFragments.find(modEvents[i].eventId);
|
|
uint32_t numFragments = (it != eventFragments.end()) ? it->second : 0;
|
|
if(numFragments <= 1)
|
|
{
|
|
// keep the historyIndex in sync with the event index
|
|
while(historyIndex < history.size() && modEvents[i].eventId == history[historyIndex].eventId)
|
|
{
|
|
++historyIndex;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
for(; historyIndex < history.size() && modEvents[i].eventId == history[historyIndex].eventId;
|
|
++historyIndex)
|
|
{
|
|
if(historyIndex == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(driver->glIsEnabled(eGL_DEPTH_TEST))
|
|
{
|
|
int depthFunc;
|
|
driver->glGetIntegerv(eGL_DEPTH_FUNC, &depthFunc);
|
|
|
|
history[historyIndex].depthTestFailed = !depthTestPassed(
|
|
depthFunc, history[historyIndex].shaderOut.depth, history[historyIndex].preMod.depth);
|
|
}
|
|
else
|
|
{
|
|
// since there is no depth test, there is no failure.
|
|
history[historyIndex].depthTestFailed = false;
|
|
}
|
|
}
|
|
|
|
if(i < modEvents.size() - 1)
|
|
{
|
|
driver->ReplayLog(modEvents[i].eventId + 1, modEvents[i + 1].eventId, eReplay_WithoutDraw);
|
|
}
|
|
}
|
|
}
|
|
|
|
}; // end of anonymous namespace
|
|
|
|
rdcarray<PixelModification> GLReplay::PixelHistory(rdcarray<EventUsage> events, ResourceId target,
|
|
uint32_t x, uint32_t y, const Subresource &sub,
|
|
CompType typeCast)
|
|
{
|
|
rdcarray<PixelModification> history;
|
|
|
|
if(events.empty())
|
|
return history;
|
|
|
|
TextureDescription textureDesc = GetTexture(target);
|
|
if(textureDesc.format.type == ResourceFormatType::Undefined)
|
|
return history;
|
|
|
|
// When RenderDoc passed y, the value being passed in is with the Y axis starting from the top
|
|
// However, we need to have it starting from the bottom, so flip it by subtracting y from the
|
|
// height.
|
|
uint32_t flippedY = (textureDesc.height >> sub.mip) - y - 1;
|
|
|
|
rdcstr regionName = StringFormat::Fmt(
|
|
"PixelHistory: pixel: (%u, %u) on %s subresource (%u, %u, %u) cast to %s with %zu events", x,
|
|
flippedY, ToStr(target).c_str(), sub.mip, sub.slice, sub.sample, ToStr(typeCast).c_str(),
|
|
events.size());
|
|
|
|
RDCDEBUG("%s", regionName.c_str());
|
|
|
|
uint32_t sampleIdx = sub.sample;
|
|
|
|
if(sampleIdx > textureDesc.msSamp)
|
|
{
|
|
sampleIdx = 0; // if the sample index is out of range (ie. average value) then default to 0
|
|
}
|
|
|
|
uint32_t sampleMask = ~0U;
|
|
if(sampleIdx < 32)
|
|
{
|
|
sampleMask = 1U << sampleIdx;
|
|
}
|
|
|
|
GLPixelHistoryResources resources;
|
|
|
|
MakeCurrentReplayContext(&m_ReplayCtx);
|
|
|
|
int glslVersion = DebugData.glslVersion;
|
|
bool usingFloatForPrimitiveId = glslVersion < 330;
|
|
|
|
PixelHistorySetupResources(m_pDriver, resources, textureDesc, sub, (uint32_t)events.size(),
|
|
glslVersion, textureDesc.msSamp);
|
|
|
|
rdcarray<EventUsage> modEvents =
|
|
QueryModifyingEvents(m_pDriver, resources, events, x, flippedY, history);
|
|
|
|
if(modEvents.empty())
|
|
{
|
|
PixelHistoryDestroyResources(m_pDriver, resources);
|
|
return history;
|
|
}
|
|
|
|
QueryFailedTests(m_pDriver, resources, modEvents, x, flippedY, history, sampleIdx);
|
|
QueryPostModPixelValues(m_pDriver, resources, modEvents, x, flippedY, history, textureDesc.msSamp,
|
|
sampleIdx);
|
|
|
|
std::map<uint32_t, uint32_t> eventFragments = QueryNumFragmentsByEvent(
|
|
m_pDriver, resources, modEvents, history, x, flippedY, textureDesc.msSamp, sampleIdx);
|
|
|
|
// copy history entries to create one history per fragment
|
|
for(size_t h = 0; h < history.size();)
|
|
{
|
|
uint32_t frags = 1;
|
|
auto it = eventFragments.find(history[h].eventId);
|
|
if(it != eventFragments.end())
|
|
{
|
|
frags = it->second;
|
|
}
|
|
for(uint32_t f = 1; f < frags; ++f)
|
|
{
|
|
history.insert(h + 1, history[h]);
|
|
}
|
|
for(uint32_t f = 0; f < frags; ++f)
|
|
{
|
|
history[h + f].fragIndex = f;
|
|
}
|
|
h += RDCMAX(1u, frags);
|
|
}
|
|
|
|
QueryShaderOutPerFragment(m_pDriver, this, resources, modEvents, x, flippedY, history,
|
|
eventFragments, textureDesc.msSamp, sampleIdx);
|
|
QueryPrimitiveIdPerFragment(m_pDriver, this, resources, modEvents, x, flippedY, history,
|
|
eventFragments, usingFloatForPrimitiveId, textureDesc.msSamp,
|
|
sampleIdx);
|
|
QueryPostModPerFragment(m_pDriver, this, resources, modEvents, x, flippedY, history,
|
|
eventFragments, textureDesc.msSamp, sampleIdx);
|
|
|
|
// copy the postMod to next history's preMod
|
|
for(size_t i = 1; i < history.size(); ++i)
|
|
{
|
|
history[i].preMod = history[i - 1].postMod;
|
|
}
|
|
|
|
CalculateFragmentDepthTests(m_pDriver, resources, modEvents, history, eventFragments);
|
|
|
|
PixelHistoryDestroyResources(m_pDriver, resources);
|
|
return history;
|
|
}
|