mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-05 01:20:42 +00:00
8318720561
* Still missing several features: - solid shading of 'secondary' data - highlighting of vertices/faces/supporting faces - helpers like axis markers or frustum - post VS data and re-projection
2259 lines
69 KiB
C++
2259 lines
69 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* 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 "gl_replay.h"
|
|
#include "gl_driver.h"
|
|
#include "gl_resources.h"
|
|
|
|
#include "data/glsl/debuguniforms.h"
|
|
|
|
#include "serialise/string_utils.h"
|
|
|
|
GLReplay::GLReplay()
|
|
{
|
|
m_pDriver = NULL;
|
|
m_Proxy = false;
|
|
|
|
RDCEraseEl(m_ReplayCtx);
|
|
m_DebugCtx = NULL;
|
|
|
|
m_OutputWindowID = 1;
|
|
}
|
|
|
|
void GLReplay::Shutdown()
|
|
{
|
|
DeleteDebugData();
|
|
|
|
DestroyOutputWindow(m_DebugID);
|
|
|
|
CloseReplayContext();
|
|
|
|
delete m_pDriver;
|
|
}
|
|
|
|
#pragma region Implemented
|
|
|
|
void GLReplay::ReadLogInitialisation()
|
|
{
|
|
MakeCurrentReplayContext(&m_ReplayCtx);
|
|
m_pDriver->ReadLogInitialisation();
|
|
}
|
|
|
|
void GLReplay::ReplayLog(uint32_t frameID, uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType)
|
|
{
|
|
MakeCurrentReplayContext(&m_ReplayCtx);
|
|
m_pDriver->ReplayLog(frameID, startEventID, endEventID, replayType);
|
|
}
|
|
|
|
vector<FetchFrameRecord> GLReplay::GetFrameRecord()
|
|
{
|
|
return m_pDriver->GetFrameRecord();
|
|
}
|
|
|
|
ResourceId GLReplay::GetLiveID(ResourceId id)
|
|
{
|
|
return m_pDriver->GetResourceManager()->GetLiveID(id);
|
|
}
|
|
|
|
APIProperties GLReplay::GetAPIProperties()
|
|
{
|
|
APIProperties ret;
|
|
|
|
ret.pipelineType = ePipelineState_OpenGL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
vector<ResourceId> GLReplay::GetBuffers()
|
|
{
|
|
vector<ResourceId> ret;
|
|
|
|
for(auto it=m_pDriver->m_Buffers.begin(); it != m_pDriver->m_Buffers.end(); ++it)
|
|
ret.push_back(it->first);
|
|
|
|
return ret;
|
|
}
|
|
|
|
vector<ResourceId> GLReplay::GetTextures()
|
|
{
|
|
vector<ResourceId> ret;
|
|
ret.reserve(m_pDriver->m_Textures.size());
|
|
|
|
for(auto it=m_pDriver->m_Textures.begin(); it != m_pDriver->m_Textures.end(); ++it)
|
|
{
|
|
auto &res = m_pDriver->m_Textures[it->first];
|
|
|
|
// skip textures that aren't from the log (except the 'default backbuffer' textures)
|
|
if(res.resource.name != m_pDriver->m_FakeBB_Color &&
|
|
res.resource.name != m_pDriver->m_FakeBB_DepthStencil &&
|
|
m_pDriver->GetResourceManager()->GetOriginalID(it->first) == it->first) continue;
|
|
|
|
ret.push_back(it->first);
|
|
CacheTexture(it->first);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void GLReplay::SetReplayData(GLWindowingData data)
|
|
{
|
|
m_ReplayCtx = data;
|
|
|
|
InitDebugData();
|
|
}
|
|
|
|
void GLReplay::InitCallstackResolver()
|
|
{
|
|
m_pDriver->GetSerialiser()->InitCallstackResolver();
|
|
}
|
|
|
|
bool GLReplay::HasCallstacks()
|
|
{
|
|
return m_pDriver->GetSerialiser()->HasCallstacks();
|
|
}
|
|
|
|
Callstack::StackResolver *GLReplay::GetCallstackResolver()
|
|
{
|
|
return m_pDriver->GetSerialiser()->GetCallstackResolver();
|
|
}
|
|
|
|
void GLReplay::CreateOutputWindowBackbuffer(OutputWindow &outwin)
|
|
{
|
|
if(m_pDriver == NULL) return;
|
|
|
|
MakeCurrentReplayContext(m_DebugCtx);
|
|
|
|
WrappedOpenGL &gl = *m_pDriver;
|
|
|
|
// create fake backbuffer for this output window.
|
|
// We'll make an FBO for this backbuffer on the replay context, so we can
|
|
// use the replay context to do the hard work of rendering to it, then just
|
|
// blit across to the real default framebuffer on the output window context
|
|
gl.glGenFramebuffers(1, &outwin.BlitData.windowFBO);
|
|
gl.glBindFramebuffer(eGL_FRAMEBUFFER, outwin.BlitData.windowFBO);
|
|
|
|
gl.glGenTextures(1, &outwin.BlitData.backbuffer);
|
|
gl.glBindTexture(eGL_TEXTURE_2D, outwin.BlitData.backbuffer);
|
|
|
|
gl.glTexStorage2D(eGL_TEXTURE_2D, 1, eGL_SRGB8, outwin.width, outwin.height);
|
|
gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MIN_FILTER, eGL_NEAREST);
|
|
gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAG_FILTER, eGL_NEAREST);
|
|
gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_WRAP_S, eGL_CLAMP_TO_EDGE);
|
|
gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_WRAP_T, eGL_CLAMP_TO_EDGE);
|
|
gl.glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, outwin.BlitData.backbuffer, 0);
|
|
|
|
outwin.BlitData.replayFBO = 0;
|
|
}
|
|
|
|
void GLReplay::InitOutputWindow(OutputWindow &outwin)
|
|
{
|
|
if(m_pDriver == NULL) return;
|
|
|
|
MakeCurrentReplayContext(&outwin);
|
|
|
|
WrappedOpenGL &gl = *m_pDriver;
|
|
|
|
gl.glGenVertexArrays(1, &outwin.BlitData.emptyVAO);
|
|
gl.glBindVertexArray(outwin.BlitData.emptyVAO);
|
|
}
|
|
|
|
bool GLReplay::CheckResizeOutputWindow(uint64_t id)
|
|
{
|
|
if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
|
|
return false;
|
|
|
|
OutputWindow &outw = m_OutputWindows[id];
|
|
|
|
if(outw.wnd == 0)
|
|
return false;
|
|
|
|
int32_t w, h;
|
|
GetOutputWindowDimensions(id, w, h);
|
|
|
|
if(w != outw.width || h != outw.height)
|
|
{
|
|
outw.width = w;
|
|
outw.height = h;
|
|
|
|
MakeCurrentReplayContext(m_DebugCtx);
|
|
|
|
WrappedOpenGL &gl = *m_pDriver;
|
|
|
|
gl.glDeleteTextures(1, &outw.BlitData.backbuffer);
|
|
gl.glDeleteFramebuffers(1, &outw.BlitData.windowFBO);
|
|
|
|
CreateOutputWindowBackbuffer(outw);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void GLReplay::BindOutputWindow(uint64_t id, bool depth)
|
|
{
|
|
if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
|
|
return;
|
|
|
|
OutputWindow &outw = m_OutputWindows[id];
|
|
|
|
MakeCurrentReplayContext(m_DebugCtx);
|
|
|
|
m_pDriver->glBindFramebuffer(eGL_FRAMEBUFFER, outw.BlitData.windowFBO);
|
|
m_pDriver->glViewport(0, 0, outw.width, outw.height);
|
|
|
|
DebugData.outWidth = float(outw.width); DebugData.outHeight = float(outw.height);
|
|
}
|
|
|
|
void GLReplay::ClearOutputWindowColour(uint64_t id, float col[4])
|
|
{
|
|
if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
|
|
return;
|
|
|
|
OutputWindow &outw = m_OutputWindows[id];
|
|
|
|
MakeCurrentReplayContext(m_DebugCtx);
|
|
|
|
m_pDriver->glClearBufferfv(eGL_COLOR, 0, col);
|
|
}
|
|
|
|
void GLReplay::ClearOutputWindowDepth(uint64_t id, float depth, uint8_t stencil)
|
|
{
|
|
if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
|
|
return;
|
|
|
|
OutputWindow &outw = m_OutputWindows[id];
|
|
|
|
MakeCurrentReplayContext(&outw);
|
|
|
|
m_pDriver->glClearBufferfv(eGL_DEPTH, 0, &depth);
|
|
}
|
|
|
|
void GLReplay::FlipOutputWindow(uint64_t id)
|
|
{
|
|
if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
|
|
return;
|
|
|
|
OutputWindow &outw = m_OutputWindows[id];
|
|
|
|
MakeCurrentReplayContext(&outw);
|
|
|
|
WrappedOpenGL &gl = *m_pDriver;
|
|
|
|
// go directly to real function so we don't try to bind the 'fake' backbuffer FBO.
|
|
gl.m_Real.glBindFramebuffer(eGL_FRAMEBUFFER, 0);
|
|
gl.glViewport(0, 0, outw.width, outw.height);
|
|
|
|
gl.glUseProgram(DebugData.blitProg);
|
|
|
|
gl.glActiveTexture(eGL_TEXTURE0);
|
|
gl.glBindTexture(eGL_TEXTURE_2D, outw.BlitData.backbuffer);
|
|
gl.glEnable(eGL_FRAMEBUFFER_SRGB);
|
|
|
|
gl.glBindVertexArray(outw.BlitData.emptyVAO);
|
|
gl.glDrawArrays(eGL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
SwapBuffers(&outw);
|
|
}
|
|
|
|
vector<byte> GLReplay::GetBufferData(ResourceId buff, uint32_t offset, uint32_t len)
|
|
{
|
|
vector<byte> ret;
|
|
|
|
if(m_pDriver->m_Buffers.find(buff) == m_pDriver->m_Buffers.end())
|
|
{
|
|
RDCWARN("Requesting data for non-existant buffer %llu", buff);
|
|
return ret;
|
|
}
|
|
|
|
auto &buf = m_pDriver->m_Buffers[buff];
|
|
|
|
uint32_t bufsize = (uint32_t)buf.size;
|
|
|
|
if(len > 0 && offset+len > buf.size)
|
|
{
|
|
RDCWARN("Attempting to read off the end of the array. Will be clamped");
|
|
|
|
if(offset < buf.size)
|
|
len = ~0U; // min below will clamp to max size size
|
|
else
|
|
return ret; // offset past buffer size, return empty array
|
|
}
|
|
else if(len == 0)
|
|
{
|
|
len = bufsize;
|
|
}
|
|
|
|
// need to ensure len+offset doesn't overrun buffer or the glGetBufferSubData call
|
|
// will fail.
|
|
len = RDCMIN(len, bufsize-offset);
|
|
|
|
if(len == 0) return ret;
|
|
|
|
ret.resize(len);
|
|
|
|
WrappedOpenGL &gl = *m_pDriver;
|
|
|
|
MakeCurrentReplayContext(m_DebugCtx);
|
|
|
|
gl.glBindBuffer(eGL_COPY_READ_BUFFER, buf.resource.name);
|
|
|
|
gl.glGetBufferSubData(eGL_COPY_READ_BUFFER, (GLintptr)offset, (GLsizeiptr)len, &ret[0]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool GLReplay::IsRenderOutput(ResourceId id)
|
|
{
|
|
for(int32_t i=0; i < m_CurPipelineState.m_FB.m_DrawFBO.Color.count; i++)
|
|
{
|
|
if(m_CurPipelineState.m_FB.m_DrawFBO.Color[i] == id)
|
|
return true;
|
|
}
|
|
|
|
if(m_CurPipelineState.m_FB.m_DrawFBO.Depth == id ||
|
|
m_CurPipelineState.m_FB.m_DrawFBO.Stencil == id)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void GLReplay::CacheTexture(ResourceId id)
|
|
{
|
|
FetchTexture tex;
|
|
|
|
MakeCurrentReplayContext(&m_ReplayCtx);
|
|
|
|
auto &res = m_pDriver->m_Textures[id];
|
|
WrappedOpenGL &gl = *m_pDriver;
|
|
|
|
tex.ID = m_pDriver->GetResourceManager()->GetOriginalID(id);
|
|
|
|
if(res.resource.Namespace == eResUnknown || res.curType == eGL_NONE)
|
|
{
|
|
if(res.resource.Namespace == eResUnknown)
|
|
RDCERR("Details for invalid texture id %llu requested", id);
|
|
|
|
tex.name = "<Uninitialised Texture>";
|
|
tex.customName = false;
|
|
tex.format = ResourceFormat();
|
|
tex.dimension = 1;
|
|
tex.resType = eResType_None;
|
|
tex.width = tex.height = tex.depth = 1;
|
|
tex.cubemap = false;
|
|
tex.mips = 1;
|
|
tex.arraysize = 1;
|
|
tex.numSubresources = 1;
|
|
tex.creationFlags = 0;
|
|
tex.msQual = 0;
|
|
tex.msSamp = 1;
|
|
tex.byteSize = 1;
|
|
|
|
m_CachedTextures[id] = tex;
|
|
return;
|
|
}
|
|
|
|
if(res.resource.Namespace == eResRenderbuffer || res.curType == eGL_RENDERBUFFER)
|
|
{
|
|
tex.dimension = 2;
|
|
tex.resType = eResType_Texture2D;
|
|
tex.width = res.width;
|
|
tex.height = res.height;
|
|
tex.depth = 1;
|
|
tex.cubemap = false;
|
|
tex.mips = 1;
|
|
tex.arraysize = 1;
|
|
tex.numSubresources = 1;
|
|
tex.creationFlags = eTextureCreate_RTV;
|
|
tex.msQual = 0;
|
|
tex.msSamp = res.samples;
|
|
|
|
tex.format = MakeResourceFormat(gl, eGL_TEXTURE_2D, res.internalFormat);
|
|
|
|
if(IsDepthStencilFormat(res.internalFormat))
|
|
tex.creationFlags |= eTextureCreate_DSV;
|
|
|
|
tex.byteSize = (tex.width*tex.height)*(tex.format.compByteWidth*tex.format.compCount);
|
|
|
|
string str = "";
|
|
char name[128] = {0};
|
|
gl.glGetObjectLabel(eGL_RENDERBUFFER, res.resource.name, 127, NULL, name);
|
|
str = name;
|
|
tex.customName = true;
|
|
|
|
if(str == "")
|
|
{
|
|
const char *suffix = "";
|
|
const char *ms = "";
|
|
|
|
if(tex.msSamp > 1)
|
|
ms = "MS";
|
|
|
|
if(tex.creationFlags & eTextureCreate_RTV)
|
|
suffix = " RTV";
|
|
if(tex.creationFlags & eTextureCreate_DSV)
|
|
suffix = " DSV";
|
|
|
|
tex.customName = false;
|
|
|
|
str = StringFormat::Fmt("Renderbuffer%s%s %llu", ms, suffix, tex.ID);
|
|
}
|
|
|
|
tex.name = str;
|
|
|
|
m_CachedTextures[id] = tex;
|
|
return;
|
|
}
|
|
|
|
GLenum target = TextureTarget(res.curType);
|
|
|
|
GLenum levelQueryType = target;
|
|
if(levelQueryType == eGL_TEXTURE_CUBE_MAP)
|
|
levelQueryType = eGL_TEXTURE_CUBE_MAP_POSITIVE_X;
|
|
|
|
GLint width = 1, height = 1, depth = 1, samples=1;
|
|
gl.glGetTextureLevelParameterivEXT(res.resource.name, levelQueryType, 0, eGL_TEXTURE_WIDTH, &width);
|
|
gl.glGetTextureLevelParameterivEXT(res.resource.name, levelQueryType, 0, eGL_TEXTURE_HEIGHT, &height);
|
|
gl.glGetTextureLevelParameterivEXT(res.resource.name, levelQueryType, 0, eGL_TEXTURE_DEPTH, &depth);
|
|
gl.glGetTextureLevelParameterivEXT(res.resource.name, levelQueryType, 0, eGL_TEXTURE_SAMPLES, &samples);
|
|
|
|
if(res.width == 0 && width > 0)
|
|
{
|
|
RDCWARN("TextureData::width didn't get filled out, setting at last minute");
|
|
res.width = width;
|
|
}
|
|
if(res.height == 0 && height > 0)
|
|
{
|
|
RDCWARN("TextureData::height didn't get filled out, setting at last minute");
|
|
res.height = height;
|
|
}
|
|
if(res.depth == 0 && depth > 0)
|
|
{
|
|
RDCWARN("TextureData::depth didn't get filled out, setting at last minute");
|
|
res.depth = depth;
|
|
}
|
|
|
|
// reasonably common defaults
|
|
tex.msQual = 0;
|
|
tex.msSamp = 1;
|
|
tex.width = tex.height = tex.depth = tex.arraysize = 1;
|
|
tex.cubemap = false;
|
|
|
|
switch(target)
|
|
{
|
|
case eGL_TEXTURE_BUFFER:
|
|
tex.resType = eResType_Buffer;
|
|
break;
|
|
case eGL_TEXTURE_1D:
|
|
tex.resType = eResType_Texture1D;
|
|
break;
|
|
case eGL_TEXTURE_2D:
|
|
tex.resType = eResType_Texture2D;
|
|
break;
|
|
case eGL_TEXTURE_3D:
|
|
tex.resType = eResType_Texture3D;
|
|
break;
|
|
case eGL_TEXTURE_1D_ARRAY:
|
|
tex.resType = eResType_Texture1DArray;
|
|
break;
|
|
case eGL_TEXTURE_2D_ARRAY:
|
|
tex.resType = eResType_Texture2DArray;
|
|
break;
|
|
case eGL_TEXTURE_RECTANGLE:
|
|
tex.resType = eResType_TextureRect;
|
|
break;
|
|
case eGL_TEXTURE_2D_MULTISAMPLE:
|
|
tex.resType = eResType_Texture2DMS;
|
|
break;
|
|
case eGL_TEXTURE_2D_MULTISAMPLE_ARRAY:
|
|
tex.resType = eResType_Texture2DMSArray;
|
|
break;
|
|
case eGL_TEXTURE_CUBE_MAP:
|
|
tex.resType = eResType_TextureCube;
|
|
break;
|
|
case eGL_TEXTURE_CUBE_MAP_ARRAY:
|
|
tex.resType = eResType_TextureCubeArray;
|
|
break;
|
|
|
|
default:
|
|
tex.resType = eResType_None;
|
|
RDCERR("Unexpected texture enum %s", ToStr::Get(target).c_str());
|
|
}
|
|
|
|
switch(target)
|
|
{
|
|
case eGL_TEXTURE_1D:
|
|
case eGL_TEXTURE_BUFFER:
|
|
tex.dimension = 1;
|
|
tex.width = (uint32_t)width;
|
|
break;
|
|
case eGL_TEXTURE_1D_ARRAY:
|
|
tex.dimension = 1;
|
|
tex.width = (uint32_t)width;
|
|
tex.arraysize = depth;
|
|
break;
|
|
case eGL_TEXTURE_2D:
|
|
case eGL_TEXTURE_RECTANGLE:
|
|
case eGL_TEXTURE_2D_MULTISAMPLE:
|
|
case eGL_TEXTURE_CUBE_MAP:
|
|
tex.dimension = 2;
|
|
tex.width = (uint32_t)width;
|
|
tex.height = (uint32_t)height;
|
|
tex.depth = (target == eGL_TEXTURE_CUBE_MAP ? 6 : 1);
|
|
tex.cubemap = (target == eGL_TEXTURE_CUBE_MAP);
|
|
tex.msSamp = (target == eGL_TEXTURE_2D_MULTISAMPLE ? samples : 1);
|
|
break;
|
|
case eGL_TEXTURE_2D_ARRAY:
|
|
case eGL_TEXTURE_2D_MULTISAMPLE_ARRAY:
|
|
case eGL_TEXTURE_CUBE_MAP_ARRAY:
|
|
tex.dimension = 2;
|
|
tex.width = (uint32_t)width;
|
|
tex.height = (uint32_t)height;
|
|
tex.depth = (target == eGL_TEXTURE_CUBE_MAP ? 6 : 1);
|
|
tex.arraysize = depth;
|
|
tex.cubemap = (target == eGL_TEXTURE_CUBE_MAP_ARRAY);
|
|
tex.msSamp = (target == eGL_TEXTURE_2D_MULTISAMPLE_ARRAY ? samples : 1);
|
|
break;
|
|
case eGL_TEXTURE_3D:
|
|
tex.dimension = 3;
|
|
tex.width = (uint32_t)width;
|
|
tex.height = (uint32_t)height;
|
|
tex.depth = (uint32_t)depth;
|
|
break;
|
|
|
|
default:
|
|
tex.dimension = 2;
|
|
RDCERR("Unexpected texture enum %s", ToStr::Get(target).c_str());
|
|
}
|
|
|
|
tex.creationFlags = res.creationFlags;
|
|
if(res.resource.name == gl.m_FakeBB_Color || res.resource.name == gl.m_FakeBB_DepthStencil)
|
|
tex.creationFlags |= eTextureCreate_SwapBuffer;
|
|
|
|
// surely this will be the same for each level... right? that would be insane if it wasn't
|
|
GLint fmt = 0;
|
|
gl.glGetTextureLevelParameterivEXT(res.resource.name, levelQueryType, 0, eGL_TEXTURE_INTERNAL_FORMAT, &fmt);
|
|
|
|
tex.format = MakeResourceFormat(gl, target, (GLenum)fmt);
|
|
|
|
if(tex.format.compType == eCompType_Depth)
|
|
tex.creationFlags |= eTextureCreate_DSV;
|
|
|
|
string str = "";
|
|
char name[128] = {0};
|
|
gl.glGetObjectLabel(eGL_TEXTURE, res.resource.name, 127, NULL, name);
|
|
str = name;
|
|
tex.customName = true;
|
|
|
|
if(str == "")
|
|
{
|
|
const char *suffix = "";
|
|
const char *ms = "";
|
|
|
|
if(tex.msSamp > 1)
|
|
ms = "MS";
|
|
|
|
if(tex.creationFlags & eTextureCreate_RTV)
|
|
suffix = " RTV";
|
|
if(tex.creationFlags & eTextureCreate_DSV)
|
|
suffix = " DSV";
|
|
|
|
tex.customName = false;
|
|
|
|
if(tex.cubemap)
|
|
{
|
|
if(tex.arraysize > 6)
|
|
str = StringFormat::Fmt("TextureCube%sArray%s %llu", ms, suffix, tex.ID);
|
|
else
|
|
str = StringFormat::Fmt("TextureCube%s%s %llu", ms, suffix, tex.ID);
|
|
}
|
|
else
|
|
{
|
|
if(tex.arraysize > 1)
|
|
str = StringFormat::Fmt("Texture%dD%sArray%s %llu", tex.dimension, ms, suffix, tex.ID);
|
|
else
|
|
str = StringFormat::Fmt("Texture%dD%s%s %llu", tex.dimension, ms, suffix, tex.ID);
|
|
}
|
|
}
|
|
|
|
tex.name = str;
|
|
|
|
if(target == eGL_TEXTURE_BUFFER)
|
|
{
|
|
tex.dimension = 1;
|
|
tex.width = tex.height = tex.depth = 1;
|
|
tex.cubemap = false;
|
|
tex.mips = 1;
|
|
tex.arraysize = 1;
|
|
tex.numSubresources = 1;
|
|
tex.creationFlags = eTextureCreate_SRV;
|
|
tex.msQual = tex.msSamp = 0;
|
|
tex.byteSize = 0;
|
|
|
|
gl.glGetTextureLevelParameterivEXT(res.resource.name, levelQueryType, 0, eGL_TEXTURE_BUFFER_SIZE, (GLint *)&tex.byteSize);
|
|
tex.width = uint32_t(tex.byteSize/(tex.format.compByteWidth*tex.format.compCount));
|
|
|
|
m_CachedTextures[id] = tex;
|
|
return;
|
|
}
|
|
|
|
tex.mips = GetNumMips(gl.m_Real, target, res.resource.name, tex.width, tex.height, tex.depth);
|
|
|
|
tex.numSubresources = tex.mips*tex.arraysize;
|
|
|
|
GLint compressed;
|
|
gl.glGetTextureLevelParameterivEXT(res.resource.name, levelQueryType, 0, eGL_TEXTURE_COMPRESSED, &compressed);
|
|
tex.byteSize = 0;
|
|
for(uint32_t a=0; a < tex.arraysize; a++)
|
|
{
|
|
for(uint32_t m=0; m < tex.mips; m++)
|
|
{
|
|
if(compressed)
|
|
{
|
|
gl.glGetTextureLevelParameterivEXT(res.resource.name, levelQueryType, m, eGL_TEXTURE_COMPRESSED_IMAGE_SIZE, &compressed);
|
|
tex.byteSize += compressed;
|
|
}
|
|
else if(tex.format.special)
|
|
{
|
|
tex.byteSize += GetByteSize(RDCMAX(1U, tex.width>>m), RDCMAX(1U, tex.height>>m), RDCMAX(1U, tex.depth>>m),
|
|
GetBaseFormat((GLenum)fmt), GetDataType((GLenum)fmt));
|
|
}
|
|
else
|
|
{
|
|
tex.byteSize += RDCMAX(1U, tex.width>>m)*RDCMAX(1U, tex.height>>m)*RDCMAX(1U, tex.depth>>m)*
|
|
tex.format.compByteWidth*tex.format.compCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_CachedTextures[id] = tex;
|
|
}
|
|
|
|
FetchBuffer GLReplay::GetBuffer(ResourceId id)
|
|
{
|
|
FetchBuffer ret;
|
|
|
|
MakeCurrentReplayContext(&m_ReplayCtx);
|
|
|
|
auto &res = m_pDriver->m_Buffers[id];
|
|
|
|
if(res.resource.Namespace == eResUnknown)
|
|
{
|
|
RDCERR("Details for invalid buffer id %llu requested", id);
|
|
RDCEraseEl(ret);
|
|
return ret;
|
|
}
|
|
|
|
WrappedOpenGL &gl = *m_pDriver;
|
|
|
|
ret.ID = m_pDriver->GetResourceManager()->GetOriginalID(id);
|
|
|
|
if(res.curType == eGL_NONE)
|
|
{
|
|
ret.byteSize = 0;
|
|
ret.creationFlags = 0;
|
|
ret.customName = false;
|
|
ret.length = 0;
|
|
ret.structureSize = 0;
|
|
return ret;
|
|
}
|
|
|
|
gl.glBindBuffer(res.curType, res.resource.name);
|
|
|
|
ret.structureSize = 0;
|
|
|
|
ret.creationFlags = 0;
|
|
switch(res.curType)
|
|
{
|
|
case eGL_ARRAY_BUFFER:
|
|
ret.creationFlags = eBufferCreate_VB;
|
|
break;
|
|
case eGL_ELEMENT_ARRAY_BUFFER:
|
|
ret.creationFlags = eBufferCreate_IB;
|
|
break;
|
|
case eGL_UNIFORM_BUFFER:
|
|
ret.creationFlags = eBufferCreate_CB;
|
|
break;
|
|
case eGL_SHADER_STORAGE_BUFFER:
|
|
ret.creationFlags = eBufferCreate_UAV;
|
|
break;
|
|
case eGL_DRAW_INDIRECT_BUFFER:
|
|
case eGL_DISPATCH_INDIRECT_BUFFER:
|
|
case eGL_PARAMETER_BUFFER_ARB:
|
|
ret.creationFlags = eBufferCreate_Indirect;
|
|
break;
|
|
case eGL_PIXEL_PACK_BUFFER:
|
|
case eGL_PIXEL_UNPACK_BUFFER:
|
|
case eGL_COPY_WRITE_BUFFER:
|
|
case eGL_COPY_READ_BUFFER:
|
|
case eGL_QUERY_BUFFER:
|
|
case eGL_TEXTURE_BUFFER:
|
|
case eGL_TRANSFORM_FEEDBACK_BUFFER:
|
|
case eGL_ATOMIC_COUNTER_BUFFER:
|
|
break;
|
|
default:
|
|
RDCERR("Unexpected buffer type %s", ToStr::Get(res.curType).c_str());
|
|
}
|
|
|
|
GLint size;
|
|
gl.glGetBufferParameteriv(res.curType, eGL_BUFFER_SIZE, &size);
|
|
|
|
ret.byteSize = ret.length = (uint32_t)size;
|
|
|
|
if(res.size == 0)
|
|
{
|
|
RDCWARN("BufferData::size didn't get filled out, setting at last minute");
|
|
res.size = ret.byteSize;
|
|
}
|
|
|
|
string str = "";
|
|
char name[128] = {0};
|
|
gl.glGetObjectLabel(eGL_BUFFER, res.resource.name, 127, NULL, name);
|
|
str = name;
|
|
ret.customName = true;
|
|
|
|
if(str == "")
|
|
{
|
|
ret.customName = false;
|
|
str = StringFormat::Fmt("Buffer %llu", ret.ID);
|
|
}
|
|
|
|
ret.name = str;
|
|
|
|
return ret;
|
|
}
|
|
|
|
vector<DebugMessage> GLReplay::GetDebugMessages()
|
|
{
|
|
return m_pDriver->GetDebugMessages();
|
|
}
|
|
|
|
ShaderReflection *GLReplay::GetShader(ResourceId id)
|
|
{
|
|
WrappedOpenGL &gl = *m_pDriver;
|
|
|
|
MakeCurrentReplayContext(&m_ReplayCtx);
|
|
|
|
void *ctx = m_ReplayCtx.ctx;
|
|
|
|
auto &shaderDetails = m_pDriver->m_Shaders[id];
|
|
|
|
if(shaderDetails.prog == 0)
|
|
{
|
|
RDCERR("Can't get shader details without separable program");
|
|
return NULL;
|
|
}
|
|
|
|
return &shaderDetails.reflection;
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Mostly Implemented
|
|
|
|
void GLReplay::GetMapping(WrappedOpenGL &gl, GLuint curProg, int shadIdx, ShaderReflection *refl, ShaderBindpointMapping &mapping)
|
|
{
|
|
// in case of bugs, we readback into this array instead of
|
|
GLint dummyReadback[32];
|
|
|
|
#if !defined(RELEASE)
|
|
for(size_t i=1; i < ARRAY_COUNT(dummyReadback); i++)
|
|
dummyReadback[i] = 0x6c7b8a9d;
|
|
#endif
|
|
|
|
const GLenum refEnum[] = {
|
|
eGL_REFERENCED_BY_VERTEX_SHADER,
|
|
eGL_REFERENCED_BY_TESS_CONTROL_SHADER,
|
|
eGL_REFERENCED_BY_TESS_EVALUATION_SHADER,
|
|
eGL_REFERENCED_BY_GEOMETRY_SHADER,
|
|
eGL_REFERENCED_BY_FRAGMENT_SHADER,
|
|
eGL_REFERENCED_BY_COMPUTE_SHADER,
|
|
};
|
|
|
|
create_array_uninit(mapping.Resources, refl->Resources.count);
|
|
for(int32_t i=0; i < refl->Resources.count; i++)
|
|
{
|
|
if(refl->Resources.elems[i].IsTexture)
|
|
{
|
|
// normal sampler or image load/store
|
|
|
|
GLint loc = gl.glGetUniformLocation(curProg, refl->Resources.elems[i].name.elems);
|
|
if(loc >= 0)
|
|
{
|
|
gl.glGetUniformiv(curProg, loc, dummyReadback);
|
|
mapping.Resources[i].bind = dummyReadback[0];
|
|
}
|
|
|
|
// handle sampler arrays, use the base name
|
|
string name = refl->Resources.elems[i].name.elems;
|
|
if(name.back() == ']')
|
|
{
|
|
do
|
|
{
|
|
name.pop_back();
|
|
} while(name.back() != '[');
|
|
name.pop_back();
|
|
}
|
|
|
|
GLuint idx = 0;
|
|
idx = gl.glGetProgramResourceIndex(curProg, eGL_UNIFORM, name.c_str());
|
|
|
|
if(idx == GL_INVALID_INDEX)
|
|
{
|
|
mapping.Resources[i].used = false;
|
|
}
|
|
else
|
|
{
|
|
GLint used = 0;
|
|
gl.glGetProgramResourceiv(curProg, eGL_UNIFORM, idx, 1, &refEnum[shadIdx], 1, NULL, &used);
|
|
mapping.Resources[i].used = (used != 0);
|
|
}
|
|
}
|
|
else if(refl->Resources.elems[i].IsReadWrite && !refl->Resources.elems[i].IsTexture)
|
|
{
|
|
if(refl->Resources.elems[i].variableType.descriptor.cols == 1 &&
|
|
refl->Resources.elems[i].variableType.descriptor.rows == 1 &&
|
|
refl->Resources.elems[i].variableType.descriptor.type == eVar_UInt)
|
|
{
|
|
// atomic uint
|
|
GLuint idx = gl.glGetProgramResourceIndex(curProg, eGL_UNIFORM, refl->Resources.elems[i].name.elems);
|
|
|
|
if(idx == GL_INVALID_INDEX)
|
|
{
|
|
mapping.Resources[i].bind = -1;
|
|
mapping.Resources[i].used = false;
|
|
}
|
|
else
|
|
{
|
|
GLenum prop = eGL_ATOMIC_COUNTER_BUFFER_INDEX;
|
|
GLuint atomicIndex;
|
|
gl.glGetProgramResourceiv(curProg, eGL_UNIFORM, idx, 1, &prop, 1, NULL, (GLint *)&atomicIndex);
|
|
|
|
if(atomicIndex == GL_INVALID_INDEX)
|
|
{
|
|
mapping.Resources[i].bind = -1;
|
|
mapping.Resources[i].used = false;
|
|
}
|
|
else
|
|
{
|
|
const GLenum atomicRefEnum[] = {
|
|
eGL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER,
|
|
eGL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER,
|
|
eGL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER,
|
|
eGL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER,
|
|
eGL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER,
|
|
eGL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER,
|
|
};
|
|
gl.glGetActiveAtomicCounterBufferiv(curProg, atomicIndex, eGL_ATOMIC_COUNTER_BUFFER_BINDING, &mapping.Resources[i].bind);
|
|
GLint used = 0;
|
|
gl.glGetActiveAtomicCounterBufferiv(curProg, atomicIndex, atomicRefEnum[shadIdx], &used);
|
|
mapping.Resources[i].used = (used != 0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// shader storage buffer object
|
|
GLuint idx = gl.glGetProgramResourceIndex(curProg, eGL_SHADER_STORAGE_BLOCK, refl->Resources.elems[i].name.elems);
|
|
|
|
if(idx == GL_INVALID_INDEX)
|
|
{
|
|
mapping.Resources[i].bind = -1;
|
|
mapping.Resources[i].used = false;
|
|
}
|
|
else
|
|
{
|
|
GLenum prop = eGL_BUFFER_BINDING;
|
|
gl.glGetProgramResourceiv(curProg, eGL_SHADER_STORAGE_BLOCK, idx, 1, &prop, 1, NULL, &mapping.Resources[i].bind);
|
|
GLint used = 0;
|
|
gl.glGetProgramResourceiv(curProg, eGL_SHADER_STORAGE_BLOCK, idx, 1, &refEnum[shadIdx], 1, NULL, &used);
|
|
mapping.Resources[i].used = (used != 0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mapping.Resources[i].bind = -1;
|
|
mapping.Resources[i].used = false;
|
|
}
|
|
}
|
|
|
|
create_array_uninit(mapping.ConstantBlocks, refl->ConstantBlocks.count);
|
|
for(int32_t i=0; i < refl->ConstantBlocks.count; i++)
|
|
{
|
|
if(refl->ConstantBlocks.elems[i].bufferBacked)
|
|
{
|
|
GLint loc = gl.glGetUniformBlockIndex(curProg, refl->ConstantBlocks.elems[i].name.elems);
|
|
if(loc >= 0)
|
|
{
|
|
gl.glGetActiveUniformBlockiv(curProg, loc, eGL_UNIFORM_BLOCK_BINDING, dummyReadback);
|
|
mapping.ConstantBlocks[i].bind = dummyReadback[0];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mapping.ConstantBlocks[i].bind = -1;
|
|
}
|
|
|
|
if(!refl->ConstantBlocks.elems[i].bufferBacked)
|
|
{
|
|
mapping.ConstantBlocks[i].used = true;
|
|
}
|
|
else
|
|
{
|
|
GLuint idx = gl.glGetProgramResourceIndex(curProg, eGL_UNIFORM_BLOCK, refl->ConstantBlocks.elems[i].name.elems);
|
|
if(idx == GL_INVALID_INDEX)
|
|
{
|
|
mapping.ConstantBlocks[i].used = false;
|
|
}
|
|
else
|
|
{
|
|
GLint used = 0;
|
|
gl.glGetProgramResourceiv(curProg, eGL_UNIFORM_BLOCK, idx, 1, &refEnum[shadIdx], 1, NULL, &used);
|
|
mapping.ConstantBlocks[i].used = (used != 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
GLint numVAttribBindings = 16;
|
|
gl.glGetIntegerv(eGL_MAX_VERTEX_ATTRIBS, &numVAttribBindings);
|
|
|
|
create_array_uninit(mapping.InputAttributes, numVAttribBindings);
|
|
for(int32_t i=0; i < numVAttribBindings; i++)
|
|
mapping.InputAttributes[i] = -1;
|
|
|
|
// override identity map with bindings
|
|
if(shadIdx == 0)
|
|
{
|
|
for(int32_t i=0; i < refl->InputSig.count; i++)
|
|
{
|
|
GLint loc = gl.glGetAttribLocation(curProg, refl->InputSig.elems[i].varName.elems);
|
|
|
|
if(loc >= 0 && loc < numVAttribBindings)
|
|
{
|
|
mapping.InputAttributes[loc] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !defined(RELEASE)
|
|
for(size_t i=1; i < ARRAY_COUNT(dummyReadback); i++)
|
|
if(dummyReadback[i] != 0x6c7b8a9d)
|
|
RDCERR("Invalid uniform readback - data beyond first element modified!");
|
|
#endif
|
|
}
|
|
|
|
void GLReplay::SavePipelineState()
|
|
{
|
|
GLPipelineState &pipe = m_CurPipelineState;
|
|
WrappedOpenGL &gl = *m_pDriver;
|
|
GLResourceManager *rm = m_pDriver->GetResourceManager();
|
|
|
|
MakeCurrentReplayContext(&m_ReplayCtx);
|
|
|
|
GLRenderState rs(&gl.GetHookset(), NULL, READING);
|
|
rs.FetchState(m_ReplayCtx.ctx, &gl);
|
|
|
|
// Index buffer
|
|
|
|
void *ctx = m_ReplayCtx.ctx;
|
|
|
|
GLuint ibuffer = 0;
|
|
gl.glGetIntegerv(eGL_ELEMENT_ARRAY_BUFFER_BINDING, (GLint*)&ibuffer);
|
|
pipe.m_VtxIn.ibuffer = rm->GetOriginalID(rm->GetID(BufferRes(ctx, ibuffer)));
|
|
|
|
pipe.m_VtxIn.primitiveRestart = rs.Enabled[GLRenderState::eEnabled_PrimitiveRestart];
|
|
pipe.m_VtxIn.restartIndex = rs.Enabled[GLRenderState::eEnabled_PrimitiveRestartFixedIndex] ? ~0U : rs.PrimitiveRestartIndex;
|
|
|
|
// Vertex buffers and attributes
|
|
GLint numVBufferBindings = 16;
|
|
gl.glGetIntegerv(eGL_MAX_VERTEX_ATTRIB_BINDINGS, &numVBufferBindings);
|
|
|
|
GLint numVAttribBindings = 16;
|
|
gl.glGetIntegerv(eGL_MAX_VERTEX_ATTRIBS, &numVAttribBindings);
|
|
|
|
create_array_uninit(pipe.m_VtxIn.vbuffers, numVBufferBindings);
|
|
create_array_uninit(pipe.m_VtxIn.attributes, numVAttribBindings);
|
|
|
|
for(GLuint i=0; i < (GLuint)numVBufferBindings; i++)
|
|
{
|
|
GLuint buffer = GetBoundVertexBuffer(gl.m_Real, i);
|
|
|
|
pipe.m_VtxIn.vbuffers[i].Buffer = rm->GetOriginalID(rm->GetID(BufferRes(ctx, buffer)));
|
|
|
|
gl.glGetIntegeri_v(eGL_VERTEX_BINDING_STRIDE, i, (GLint *)&pipe.m_VtxIn.vbuffers[i].Stride);
|
|
gl.glGetIntegeri_v(eGL_VERTEX_BINDING_OFFSET, i, (GLint *)&pipe.m_VtxIn.vbuffers[i].Offset);
|
|
gl.glGetIntegeri_v(eGL_VERTEX_BINDING_DIVISOR, i, (GLint *)&pipe.m_VtxIn.vbuffers[i].Divisor);
|
|
}
|
|
|
|
for(GLuint i=0; i < (GLuint)numVAttribBindings; i++)
|
|
{
|
|
gl.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_ENABLED, (GLint *)&pipe.m_VtxIn.attributes[i].Enabled);
|
|
gl.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_BINDING, (GLint *)&pipe.m_VtxIn.attributes[i].BufferSlot);
|
|
gl.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_RELATIVE_OFFSET, (GLint*)&pipe.m_VtxIn.attributes[i].RelativeOffset);
|
|
|
|
GLenum type = eGL_FLOAT;
|
|
GLint normalized = 0;
|
|
|
|
gl.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_TYPE, (GLint *)&type);
|
|
gl.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &normalized);
|
|
|
|
GLint integer = 0;
|
|
gl.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_INTEGER, &integer);
|
|
|
|
RDCEraseEl(pipe.m_VtxIn.attributes[i].GenericValue);
|
|
gl.glGetVertexAttribfv(i, eGL_CURRENT_VERTEX_ATTRIB, &pipe.m_VtxIn.attributes[i].GenericValue.x);
|
|
|
|
ResourceFormat fmt;
|
|
|
|
fmt.special = false;
|
|
fmt.compCount = 4;
|
|
gl.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_SIZE, (GLint *)&fmt.compCount);
|
|
|
|
bool intComponent = !normalized || integer;
|
|
|
|
switch(type)
|
|
{
|
|
default:
|
|
case eGL_BYTE:
|
|
fmt.compByteWidth = 1;
|
|
fmt.compType = intComponent ? eCompType_SInt : eCompType_SNorm;
|
|
fmt.strname = (fmt.compCount > 1 ? StringFormat::Fmt("GL_BYTE%d", fmt.compCount) : string("GL_BYTE")) + (intComponent ? "" : "_SNORM");
|
|
break;
|
|
case eGL_UNSIGNED_BYTE:
|
|
fmt.compByteWidth = 1;
|
|
fmt.compType = intComponent ? eCompType_UInt : eCompType_UNorm;
|
|
fmt.strname = (fmt.compCount > 1 ? StringFormat::Fmt("GL_UNSIGNED_BYTE%d", fmt.compCount) : string("GL_UNSIGNED_BYTE")) + (intComponent ? "" : "_UNORM");
|
|
break;
|
|
case eGL_SHORT:
|
|
fmt.compByteWidth = 2;
|
|
fmt.compType = intComponent ? eCompType_SInt : eCompType_SNorm;
|
|
fmt.strname = (fmt.compCount > 1 ? StringFormat::Fmt("GL_SHORT%d", fmt.compCount) : string("GL_SHORT")) + (intComponent ? "" : "_SNORM");
|
|
break;
|
|
case eGL_UNSIGNED_SHORT:
|
|
fmt.compByteWidth = 2;
|
|
fmt.compType = intComponent ? eCompType_UInt : eCompType_UNorm;
|
|
fmt.strname = (fmt.compCount > 1 ? StringFormat::Fmt("GL_UNSIGNED_SHORT%d", fmt.compCount) : string("GL_UNSIGNED_SHORT")) + (intComponent ? "" : "_UNORM");
|
|
break;
|
|
case eGL_INT:
|
|
fmt.compByteWidth = 4;
|
|
fmt.compType = intComponent ? eCompType_SInt : eCompType_SNorm;
|
|
fmt.strname = (fmt.compCount > 1 ? StringFormat::Fmt("GL_INT%d", fmt.compCount) : string("GL_INT")) + (intComponent ? "" : "_SNORM");
|
|
break;
|
|
case eGL_UNSIGNED_INT:
|
|
fmt.compByteWidth = 4;
|
|
fmt.compType = intComponent ? eCompType_UInt : eCompType_UNorm;
|
|
fmt.strname = (fmt.compCount > 1 ? StringFormat::Fmt("GL_UNSIGNED_INT%d", fmt.compCount) : string("GL_UNSIGNED_INT")) + (intComponent ? "" : "_UNORM");
|
|
break;
|
|
case eGL_FLOAT:
|
|
fmt.compByteWidth = 4;
|
|
fmt.compType = eCompType_Float;
|
|
fmt.strname = (fmt.compCount > 1 ? StringFormat::Fmt("GL_FLOAT%d", fmt.compCount) : string("GL_FLOAT"));
|
|
break;
|
|
case eGL_DOUBLE:
|
|
fmt.compByteWidth = 8;
|
|
fmt.compType = eCompType_Double;
|
|
fmt.strname = (fmt.compCount > 1 ? StringFormat::Fmt("GL_DOUBLE%d", fmt.compCount) : string("GL_DOUBLE"));
|
|
break;
|
|
case eGL_HALF_FLOAT:
|
|
fmt.compByteWidth = 2;
|
|
fmt.compType = eCompType_Float;
|
|
fmt.strname = (fmt.compCount > 1 ? StringFormat::Fmt("GL_HALF_FLOAT%d", fmt.compCount) : string("GL_HALF_FLOAT"));
|
|
break;
|
|
case eGL_INT_2_10_10_10_REV:
|
|
fmt.special = true;
|
|
fmt.specialFormat = eSpecial_R10G10B10A2;
|
|
fmt.compCount = 4;
|
|
fmt.compType = eCompType_UInt;
|
|
fmt.strname = "GL_INT_2_10_10_10_REV";
|
|
break;
|
|
case eGL_UNSIGNED_INT_2_10_10_10_REV:
|
|
fmt.special = true;
|
|
fmt.specialFormat = eSpecial_R10G10B10A2;
|
|
fmt.compCount = 4;
|
|
fmt.compType = eCompType_SInt;
|
|
fmt.strname = "GL_UNSIGNED_INT_2_10_10_10_REV";
|
|
break;
|
|
case eGL_UNSIGNED_INT_10F_11F_11F_REV:
|
|
fmt.special = true;
|
|
fmt.specialFormat = eSpecial_R11G11B10;
|
|
fmt.compCount = 3;
|
|
fmt.compType = eCompType_Float;
|
|
fmt.strname = "GL_UNSIGNED_INT_10F_11F_11F_REV";
|
|
break;
|
|
}
|
|
|
|
if(fmt.compCount == eGL_BGRA)
|
|
{
|
|
fmt.compCount = 4;
|
|
fmt.special = true;
|
|
fmt.specialFormat = eSpecial_B8G8R8A8;
|
|
fmt.compType = eCompType_UNorm;
|
|
|
|
if(type == eGL_UNSIGNED_BYTE)
|
|
{
|
|
fmt.specialFormat = eSpecial_B8G8R8A8;
|
|
fmt.compType = eCompType_UNorm;
|
|
fmt.strname = "GL_BGRA8";
|
|
}
|
|
else if(type == eGL_UNSIGNED_INT_2_10_10_10_REV || type == eGL_INT_2_10_10_10_REV)
|
|
{
|
|
fmt.specialFormat = eSpecial_R10G10B10A2;
|
|
fmt.compType = type == eGL_UNSIGNED_INT_2_10_10_10_REV ? eCompType_UInt : eCompType_SInt;
|
|
fmt.strname = type == eGL_UNSIGNED_INT_2_10_10_10_REV ? "GL_UNSIGNED_INT_2_10_10_10_REV" : "GL_INT_2_10_10_10_REV";
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Unexpected BGRA type");
|
|
}
|
|
RDCASSERT(type == eGL_UNSIGNED_BYTE);
|
|
}
|
|
|
|
pipe.m_VtxIn.attributes[i].Format = fmt;
|
|
}
|
|
|
|
pipe.m_VtxIn.provokingVertexLast = (rs.ProvokingVertex != eGL_FIRST_VERTEX_CONVENTION);
|
|
|
|
memcpy(pipe.m_VtxProcess.defaultInnerLevel, rs.PatchParams.defaultInnerLevel, sizeof(rs.PatchParams.defaultInnerLevel));
|
|
memcpy(pipe.m_VtxProcess.defaultOuterLevel, rs.PatchParams.defaultOuterLevel, sizeof(rs.PatchParams.defaultOuterLevel));
|
|
|
|
pipe.m_VtxProcess.discard = rs.Enabled[GLRenderState::eEnabled_RasterizerDiscard];
|
|
pipe.m_VtxProcess.clipOriginLowerLeft = (rs.ClipOrigin != eGL_UPPER_LEFT);
|
|
pipe.m_VtxProcess.clipNegativeOneToOne = (rs.ClipDepth != eGL_ZERO_TO_ONE);
|
|
for(int i=0; i < 8; i++)
|
|
pipe.m_VtxProcess.clipPlanes[i] = rs.Enabled[GLRenderState::eEnabled_ClipDistance0+i];
|
|
|
|
// Shader stages & Textures
|
|
|
|
GLint numTexUnits = 8;
|
|
gl.glGetIntegerv(eGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numTexUnits);
|
|
create_array_uninit(pipe.Textures, numTexUnits);
|
|
create_array_uninit(pipe.Samplers, numTexUnits);
|
|
|
|
GLenum activeTexture = eGL_TEXTURE0;
|
|
gl.glGetIntegerv(eGL_ACTIVE_TEXTURE, (GLint*)&activeTexture);
|
|
|
|
pipe.m_VS.stage = eShaderStage_Vertex;
|
|
pipe.m_TCS.stage = eShaderStage_Tess_Control;
|
|
pipe.m_TES.stage = eShaderStage_Tess_Eval;
|
|
pipe.m_GS.stage = eShaderStage_Geometry;
|
|
pipe.m_FS.stage = eShaderStage_Fragment;
|
|
pipe.m_CS.stage = eShaderStage_Compute;
|
|
|
|
GLuint curProg = 0;
|
|
gl.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint*)&curProg);
|
|
|
|
GLPipelineState::ShaderStage *stages[6] = {
|
|
&pipe.m_VS,
|
|
&pipe.m_TCS,
|
|
&pipe.m_TES,
|
|
&pipe.m_GS,
|
|
&pipe.m_FS,
|
|
&pipe.m_CS,
|
|
};
|
|
ShaderReflection *refls[6] = { NULL };
|
|
ShaderBindpointMapping *mappings[6] = { NULL };
|
|
|
|
for(int i=0; i < 6; i++)
|
|
{
|
|
stages[i]->Shader = ResourceId();
|
|
stages[i]->ShaderDetails = NULL;
|
|
stages[i]->BindpointMapping.ConstantBlocks.Delete();
|
|
stages[i]->BindpointMapping.Resources.Delete();
|
|
}
|
|
|
|
if(curProg == 0)
|
|
{
|
|
gl.glGetIntegerv(eGL_PROGRAM_PIPELINE_BINDING, (GLint*)&curProg);
|
|
|
|
if(curProg == 0)
|
|
{
|
|
for(GLint unit=0; unit < numTexUnits; unit++)
|
|
{
|
|
RDCEraseEl(pipe.Textures[unit]);
|
|
RDCEraseEl(pipe.Samplers[unit]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ResourceId id = rm->GetID(ProgramPipeRes(ctx, curProg));
|
|
auto &pipeDetails = m_pDriver->m_Pipelines[id];
|
|
|
|
for(size_t i=0; i < ARRAY_COUNT(pipeDetails.stageShaders); i++)
|
|
{
|
|
if(pipeDetails.stageShaders[i] != ResourceId())
|
|
{
|
|
curProg = rm->GetCurrentResource(pipeDetails.stagePrograms[i]).name;
|
|
stages[i]->Shader = rm->GetOriginalID(pipeDetails.stageShaders[i]);
|
|
refls[i] = GetShader(pipeDetails.stageShaders[i]);
|
|
GetMapping(gl, curProg, (int)i, refls[i], stages[i]->BindpointMapping);
|
|
mappings[i] = &stages[i]->BindpointMapping;
|
|
}
|
|
else
|
|
{
|
|
stages[i]->Shader = ResourceId();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto &progDetails = m_pDriver->m_Programs[rm->GetID(ProgramRes(ctx, curProg))];
|
|
|
|
for(size_t i=0; i < ARRAY_COUNT(progDetails.stageShaders); i++)
|
|
{
|
|
if(progDetails.stageShaders[i] != ResourceId())
|
|
{
|
|
stages[i]->Shader = rm->GetOriginalID(progDetails.stageShaders[i]);
|
|
refls[i] = GetShader(progDetails.stageShaders[i]);
|
|
GetMapping(gl, curProg, (int)i, refls[i], stages[i]->BindpointMapping);
|
|
mappings[i] = &stages[i]->BindpointMapping;
|
|
}
|
|
}
|
|
}
|
|
|
|
RDCEraseEl(pipe.m_Feedback);
|
|
|
|
GLuint feedback = 0;
|
|
gl.glGetIntegerv(eGL_TRANSFORM_FEEDBACK_BINDING, (GLint*)&feedback);
|
|
|
|
if(feedback != 0)
|
|
pipe.m_Feedback.Obj = rm->GetOriginalID(rm->GetID(FeedbackRes(ctx, feedback)));
|
|
|
|
GLint maxCount = 0;
|
|
gl.glGetIntegerv(eGL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &maxCount);
|
|
|
|
for(int i=0; i < (int)ARRAY_COUNT(pipe.m_Feedback.BufferBinding) && i < maxCount; i++)
|
|
{
|
|
GLuint buffer = 0;
|
|
gl.glGetIntegeri_v(eGL_TRANSFORM_FEEDBACK_BUFFER_BINDING, i, (GLint*)&buffer);
|
|
pipe.m_Feedback.BufferBinding[i] = rm->GetOriginalID(rm->GetID(BufferRes(ctx, buffer)));
|
|
gl.glGetInteger64i_v(eGL_TRANSFORM_FEEDBACK_BUFFER_START, i, (GLint64*)&pipe.m_Feedback.Offset[i]);
|
|
gl.glGetInteger64i_v(eGL_TRANSFORM_FEEDBACK_BUFFER_SIZE, i, (GLint64*)&pipe.m_Feedback.Size[i]);
|
|
}
|
|
|
|
GLint p=0;
|
|
gl.glGetIntegerv(eGL_TRANSFORM_FEEDBACK_BUFFER_PAUSED, &p);
|
|
pipe.m_Feedback.Paused = (p != 0);
|
|
|
|
gl.glGetIntegerv(eGL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE, &p);
|
|
pipe.m_Feedback.Active = (p != 0);
|
|
|
|
for(int i=0; i < 6; i++)
|
|
{
|
|
size_t num = RDCMIN(128, rs.Subroutines[i].numSubroutines);
|
|
if(num == 0)
|
|
{
|
|
RDCEraseEl(stages[i]->Subroutines);
|
|
}
|
|
else
|
|
{
|
|
create_array_uninit(stages[i]->Subroutines, num);
|
|
memcpy(stages[i]->Subroutines.elems, rs.Subroutines[i].Values, num);
|
|
}
|
|
}
|
|
|
|
// GL is ass-backwards in its handling of texture units. When a shader is active
|
|
// the types in the glsl samplers inform which targets are used from which texture units
|
|
//
|
|
// So texture unit 5 can have a 2D bound (texture 52) and a Cube bound (texture 77).
|
|
// * if a uniform sampler2D has value 5 then the 2D texture is used, and we sample from 52
|
|
// * if a uniform samplerCube has value 5 then the Cube texture is used, and we sample from 77
|
|
// It's illegal for both a sampler2D and samplerCube to both have the same value (or any two
|
|
// different types). It makes it all rather pointless and needlessly complex.
|
|
//
|
|
// What we have to do then, is consider the program, look at the values of the uniforms, and
|
|
// then get the appropriate current binding based on the uniform type. We can warn/alert the
|
|
// user if we hit the illegal case of two uniforms with different types but the same value
|
|
//
|
|
// Handling is different if no shaders are active, but we don't consider that case.
|
|
|
|
for(GLint unit=0; unit < numTexUnits; unit++)
|
|
{
|
|
GLenum binding = eGL_NONE;
|
|
GLenum target = eGL_NONE;
|
|
ShaderResourceType resType = eResType_None;
|
|
|
|
bool shadow = false;
|
|
|
|
for(size_t s=0; s < ARRAY_COUNT(refls); s++)
|
|
{
|
|
if(refls[s] == NULL) continue;
|
|
|
|
for(int32_t r=0; r < refls[s]->Resources.count; r++)
|
|
{
|
|
if(refls[s]->Resources[r].IsReadWrite) continue;
|
|
|
|
// bindPoint is the uniform value for this sampler
|
|
if(mappings[s]->Resources[ refls[s]->Resources[r].bindPoint ].bind == unit)
|
|
{
|
|
GLenum t = eGL_NONE;
|
|
|
|
if(strstr(refls[s]->Resources[r].variableType.descriptor.name.elems, "Shadow"))
|
|
shadow = true;
|
|
|
|
switch(refls[s]->Resources[r].resType)
|
|
{
|
|
case eResType_None:
|
|
target = eGL_NONE;
|
|
break;
|
|
case eResType_Buffer:
|
|
target = eGL_TEXTURE_BUFFER;
|
|
break;
|
|
case eResType_Texture1D:
|
|
target = eGL_TEXTURE_1D;
|
|
break;
|
|
case eResType_Texture1DArray:
|
|
target = eGL_TEXTURE_1D_ARRAY;
|
|
break;
|
|
case eResType_Texture2D:
|
|
target = eGL_TEXTURE_2D;
|
|
break;
|
|
case eResType_TextureRect:
|
|
target = eGL_TEXTURE_RECTANGLE;
|
|
break;
|
|
case eResType_Texture2DArray:
|
|
target = eGL_TEXTURE_2D_ARRAY;
|
|
break;
|
|
case eResType_Texture2DMS:
|
|
target = eGL_TEXTURE_2D_MULTISAMPLE;
|
|
break;
|
|
case eResType_Texture2DMSArray:
|
|
target = eGL_TEXTURE_2D_MULTISAMPLE_ARRAY;
|
|
break;
|
|
case eResType_Texture3D:
|
|
target = eGL_TEXTURE_3D;
|
|
break;
|
|
case eResType_TextureCube:
|
|
target = eGL_TEXTURE_CUBE_MAP;
|
|
break;
|
|
case eResType_TextureCubeArray:
|
|
target = eGL_TEXTURE_CUBE_MAP_ARRAY;
|
|
break;
|
|
}
|
|
|
|
if(target != eGL_NONE)
|
|
t = TextureBinding(target);
|
|
|
|
resType = refls[s]->Resources[r].resType;
|
|
|
|
if(binding == eGL_NONE)
|
|
{
|
|
binding = t;
|
|
}
|
|
else if(binding == t)
|
|
{
|
|
// two uniforms with the same type pointing to the same slot is fine
|
|
binding = t;
|
|
}
|
|
else if(binding != t)
|
|
{
|
|
RDCWARN("Two uniforms pointing to texture unit %d with types %s and %s", unit, ToStr::Get(binding).c_str(), ToStr::Get(t).c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(binding != eGL_NONE)
|
|
{
|
|
gl.glActiveTexture(GLenum(eGL_TEXTURE0+unit));
|
|
|
|
GLuint tex;
|
|
gl.glGetIntegerv(binding, (GLint *)&tex);
|
|
|
|
// very bespoke/specific
|
|
GLint firstSlice = 0;
|
|
|
|
if(target != eGL_TEXTURE_BUFFER)
|
|
gl.glGetTexParameteriv(target, eGL_TEXTURE_VIEW_MIN_LEVEL, &firstSlice);
|
|
|
|
pipe.Textures[unit].Resource = rm->GetOriginalID(rm->GetID(TextureRes(ctx, tex)));
|
|
pipe.Textures[unit].FirstSlice = (uint32_t)firstSlice;
|
|
pipe.Textures[unit].ResType = resType;
|
|
|
|
pipe.Textures[unit].DepthReadChannel = -1;
|
|
|
|
GLenum levelQueryType = target == eGL_TEXTURE_CUBE_MAP ? eGL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
|
|
GLenum fmt = eGL_NONE;
|
|
gl.glGetTexLevelParameteriv(levelQueryType, 0, eGL_TEXTURE_INTERNAL_FORMAT, (GLint *)&fmt);
|
|
if(IsDepthStencilFormat(fmt))
|
|
{
|
|
GLint depthMode;
|
|
gl.glGetTexParameteriv(target, eGL_DEPTH_STENCIL_TEXTURE_MODE, &depthMode);
|
|
|
|
if(depthMode == eGL_DEPTH_COMPONENT)
|
|
pipe.Textures[unit].DepthReadChannel = 0;
|
|
else if(depthMode == eGL_STENCIL_INDEX)
|
|
pipe.Textures[unit].DepthReadChannel = 1;
|
|
}
|
|
|
|
GLint swizzles[4] = { eGL_RED, eGL_GREEN, eGL_BLUE, eGL_ALPHA };
|
|
if(target != eGL_TEXTURE_BUFFER)
|
|
gl.glGetTexParameteriv(target, eGL_TEXTURE_SWIZZLE_RGBA, swizzles);
|
|
|
|
for(int i=0; i < 4; i++)
|
|
{
|
|
switch(swizzles[i])
|
|
{
|
|
default:
|
|
case GL_ZERO:
|
|
pipe.Textures[unit].Swizzle[i] = eSwizzle_Zero;
|
|
break;
|
|
case GL_ONE:
|
|
pipe.Textures[unit].Swizzle[i] = eSwizzle_One;
|
|
break;
|
|
case eGL_RED:
|
|
pipe.Textures[unit].Swizzle[i] = eSwizzle_Red;
|
|
break;
|
|
case eGL_GREEN:
|
|
pipe.Textures[unit].Swizzle[i] = eSwizzle_Green;
|
|
break;
|
|
case eGL_BLUE:
|
|
pipe.Textures[unit].Swizzle[i] = eSwizzle_Blue;
|
|
break;
|
|
case eGL_ALPHA:
|
|
pipe.Textures[unit].Swizzle[i] = eSwizzle_Alpha;
|
|
break;
|
|
}
|
|
}
|
|
|
|
GLuint samp;
|
|
gl.glGetIntegerv(eGL_SAMPLER_BINDING, (GLint *)&samp);
|
|
|
|
pipe.Samplers[unit].Samp = rm->GetOriginalID(rm->GetID(SamplerRes(ctx, samp)));
|
|
|
|
if(target != eGL_TEXTURE_BUFFER)
|
|
{
|
|
if(samp != 0)
|
|
gl.glGetSamplerParameterfv(samp, eGL_TEXTURE_BORDER_COLOR, &pipe.Samplers[unit].BorderColor[0]);
|
|
else
|
|
gl.glGetTexParameterfv(target, eGL_TEXTURE_BORDER_COLOR, &pipe.Samplers[unit].BorderColor[0]);
|
|
|
|
pipe.Samplers[unit].UseBorder = false;
|
|
pipe.Samplers[unit].UseComparison = shadow;
|
|
|
|
GLint v;
|
|
v=0;
|
|
if(samp != 0)
|
|
gl.glGetSamplerParameteriv(samp, eGL_TEXTURE_WRAP_S, &v);
|
|
else
|
|
gl.glGetTexParameteriv(target, eGL_TEXTURE_WRAP_S, &v);
|
|
pipe.Samplers[unit].AddressS = SamplerString((GLenum)v);
|
|
pipe.Samplers[unit].UseBorder |= (v == eGL_CLAMP_TO_BORDER);
|
|
|
|
v=0;
|
|
if(samp != 0)
|
|
gl.glGetSamplerParameteriv(samp, eGL_TEXTURE_WRAP_T, &v);
|
|
else
|
|
gl.glGetTexParameteriv(target, eGL_TEXTURE_WRAP_T, &v);
|
|
pipe.Samplers[unit].AddressT = SamplerString((GLenum)v);
|
|
pipe.Samplers[unit].UseBorder |= (v == eGL_CLAMP_TO_BORDER);
|
|
|
|
v=0;
|
|
if(samp != 0)
|
|
gl.glGetSamplerParameteriv(samp, eGL_TEXTURE_WRAP_R, &v);
|
|
else
|
|
gl.glGetTexParameteriv(target, eGL_TEXTURE_WRAP_R, &v);
|
|
pipe.Samplers[unit].AddressR = SamplerString((GLenum)v);
|
|
pipe.Samplers[unit].UseBorder |= (v == eGL_CLAMP_TO_BORDER);
|
|
|
|
v=0;
|
|
if(samp != 0)
|
|
gl.glGetSamplerParameteriv(samp, eGL_TEXTURE_CUBE_MAP_SEAMLESS, &v);
|
|
else
|
|
gl.glGetTexParameteriv(target, eGL_TEXTURE_CUBE_MAP_SEAMLESS, &v);
|
|
pipe.Samplers[unit].SeamlessCube = (v != 0 || rs.Enabled[GLRenderState::eEnabled_TexCubeSeamless]);
|
|
|
|
v=0;
|
|
if(samp != 0)
|
|
gl.glGetSamplerParameteriv(samp, eGL_TEXTURE_COMPARE_FUNC, &v);
|
|
else
|
|
gl.glGetTexParameteriv(target, eGL_TEXTURE_COMPARE_FUNC, &v);
|
|
pipe.Samplers[unit].Comparison = ToStr::Get((GLenum)v).substr(3).c_str();
|
|
|
|
v=0;
|
|
if(samp != 0)
|
|
gl.glGetSamplerParameteriv(samp, eGL_TEXTURE_MIN_FILTER, &v);
|
|
else
|
|
gl.glGetTexParameteriv(target, eGL_TEXTURE_MIN_FILTER, &v);
|
|
pipe.Samplers[unit].MinFilter = SamplerString((GLenum)v);
|
|
|
|
v=0;
|
|
if(samp != 0)
|
|
gl.glGetSamplerParameteriv(samp, eGL_TEXTURE_MAG_FILTER, &v);
|
|
else
|
|
gl.glGetTexParameteriv(target, eGL_TEXTURE_MAG_FILTER, &v);
|
|
pipe.Samplers[unit].MagFilter = SamplerString((GLenum)v);
|
|
|
|
if(samp != 0)
|
|
gl.glGetSamplerParameterfv(samp, eGL_TEXTURE_MAX_ANISOTROPY_EXT, &pipe.Samplers[unit].MaxAniso);
|
|
else
|
|
gl.glGetTexParameterfv(target, eGL_TEXTURE_MAX_ANISOTROPY_EXT, &pipe.Samplers[unit].MaxAniso);
|
|
|
|
gl.glGetTexParameterfv(target, eGL_TEXTURE_MAX_LOD, &pipe.Samplers[unit].MaxLOD);
|
|
gl.glGetTexParameterfv(target, eGL_TEXTURE_MIN_LOD, &pipe.Samplers[unit].MinLOD);
|
|
gl.glGetTexParameterfv(target, eGL_TEXTURE_LOD_BIAS, &pipe.Samplers[unit].MipLODBias);
|
|
}
|
|
else
|
|
{
|
|
// texture buffers don't support sampling
|
|
RDCEraseEl(pipe.Samplers[unit].BorderColor);
|
|
pipe.Samplers[unit].AddressS = "";
|
|
pipe.Samplers[unit].AddressT = "";
|
|
pipe.Samplers[unit].AddressR = "";
|
|
pipe.Samplers[unit].Comparison = "";
|
|
pipe.Samplers[unit].MinFilter = "";
|
|
pipe.Samplers[unit].MagFilter = "";
|
|
pipe.Samplers[unit].UseBorder = false;
|
|
pipe.Samplers[unit].UseComparison = false;
|
|
pipe.Samplers[unit].SeamlessCube = false;
|
|
pipe.Samplers[unit].MaxAniso = 0.0f;
|
|
pipe.Samplers[unit].MaxLOD = 0.0f;
|
|
pipe.Samplers[unit].MinLOD = 0.0f;
|
|
pipe.Samplers[unit].MipLODBias = 0.0f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// what should we do in this case? there could be something bound just not used,
|
|
// it'd be nice to return that
|
|
}
|
|
}
|
|
|
|
gl.glActiveTexture(activeTexture);
|
|
|
|
create_array_uninit(pipe.UniformBuffers, ARRAY_COUNT(rs.UniformBinding));
|
|
for(int32_t b=0; b < pipe.UniformBuffers.count; b++)
|
|
{
|
|
if(rs.UniformBinding[b].name == 0)
|
|
{
|
|
pipe.UniformBuffers[b].Resource = ResourceId();
|
|
pipe.UniformBuffers[b].Offset = pipe.UniformBuffers[b].Size = 0;
|
|
}
|
|
else
|
|
{
|
|
pipe.UniformBuffers[b].Resource = rm->GetOriginalID(rm->GetID(BufferRes(ctx, rs.UniformBinding[b].name)));
|
|
pipe.UniformBuffers[b].Offset = rs.UniformBinding[b].start;
|
|
pipe.UniformBuffers[b].Size = rs.UniformBinding[b].size;
|
|
}
|
|
}
|
|
|
|
create_array_uninit(pipe.AtomicBuffers, ARRAY_COUNT(rs.AtomicCounter));
|
|
for(int32_t b=0; b < pipe.AtomicBuffers.count; b++)
|
|
{
|
|
if(rs.AtomicCounter[b].name == 0)
|
|
{
|
|
pipe.AtomicBuffers[b].Resource = ResourceId();
|
|
pipe.AtomicBuffers[b].Offset = pipe.AtomicBuffers[b].Size = 0;
|
|
}
|
|
else
|
|
{
|
|
pipe.AtomicBuffers[b].Resource = rm->GetOriginalID(rm->GetID(BufferRes(ctx, rs.AtomicCounter[b].name)));
|
|
pipe.AtomicBuffers[b].Offset = rs.AtomicCounter[b].start;
|
|
pipe.AtomicBuffers[b].Size = rs.AtomicCounter[b].size;
|
|
}
|
|
}
|
|
|
|
create_array_uninit(pipe.ShaderStorageBuffers, ARRAY_COUNT(rs.ShaderStorage));
|
|
for(int32_t b=0; b < pipe.ShaderStorageBuffers.count; b++)
|
|
{
|
|
if(rs.ShaderStorage[b].name == 0)
|
|
{
|
|
pipe.ShaderStorageBuffers[b].Resource = ResourceId();
|
|
pipe.ShaderStorageBuffers[b].Offset = pipe.ShaderStorageBuffers[b].Size = 0;
|
|
}
|
|
else
|
|
{
|
|
pipe.ShaderStorageBuffers[b].Resource = rm->GetOriginalID(rm->GetID(BufferRes(ctx, rs.ShaderStorage[b].name)));
|
|
pipe.ShaderStorageBuffers[b].Offset = rs.ShaderStorage[b].start;
|
|
pipe.ShaderStorageBuffers[b].Size = rs.ShaderStorage[b].size;
|
|
}
|
|
}
|
|
|
|
create_array_uninit(pipe.Images, ARRAY_COUNT(rs.Images));
|
|
for(int32_t i=0; i < pipe.Images.count; i++)
|
|
{
|
|
if(rs.Images[i].name == 0)
|
|
{
|
|
RDCEraseEl(pipe.Images[i]);
|
|
}
|
|
else
|
|
{
|
|
ResourceId id = rm->GetID(TextureRes(ctx, rs.Images[i].name));
|
|
pipe.Images[i].Resource = rm->GetOriginalID(id);
|
|
pipe.Images[i].Level = rs.Images[i].level;
|
|
pipe.Images[i].Layered = rs.Images[i].layered;
|
|
pipe.Images[i].Layer = rs.Images[i].layer;
|
|
if(rs.Images[i].access == eGL_READ_ONLY)
|
|
{
|
|
pipe.Images[i].readAllowed = true;
|
|
pipe.Images[i].writeAllowed = false;
|
|
}
|
|
else if(rs.Images[i].access == eGL_WRITE_ONLY)
|
|
{
|
|
pipe.Images[i].readAllowed = false;
|
|
pipe.Images[i].writeAllowed = true;
|
|
}
|
|
else
|
|
{
|
|
pipe.Images[i].readAllowed = true;
|
|
pipe.Images[i].writeAllowed = true;
|
|
}
|
|
pipe.Images[i].Format = MakeResourceFormat(gl, eGL_TEXTURE_2D, rs.Images[i].format);
|
|
|
|
pipe.Images[i].ResType = m_CachedTextures[id].resType;
|
|
}
|
|
}
|
|
|
|
// Vertex post processing and rasterization
|
|
|
|
RDCCOMPILE_ASSERT(ARRAY_COUNT(rs.Viewports) == ARRAY_COUNT(rs.DepthRanges), "GL Viewport count does not match depth ranges count");
|
|
create_array_uninit(pipe.m_Rasterizer.Viewports, ARRAY_COUNT(rs.Viewports));
|
|
for (int32_t v = 0; v < pipe.m_Rasterizer.Viewports.count; ++v)
|
|
{
|
|
pipe.m_Rasterizer.Viewports[v].Left = rs.Viewports[v].x;
|
|
pipe.m_Rasterizer.Viewports[v].Bottom = rs.Viewports[v].y;
|
|
pipe.m_Rasterizer.Viewports[v].Width = rs.Viewports[v].width;
|
|
pipe.m_Rasterizer.Viewports[v].Height = rs.Viewports[v].height;
|
|
pipe.m_Rasterizer.Viewports[v].MinDepth = rs.DepthRanges[v].nearZ;
|
|
pipe.m_Rasterizer.Viewports[v].MaxDepth = rs.DepthRanges[v].farZ;
|
|
}
|
|
|
|
create_array_uninit(pipe.m_Rasterizer.Scissors, ARRAY_COUNT(rs.Scissors));
|
|
for (int32_t s = 0; s < pipe.m_Rasterizer.Scissors.count; ++s)
|
|
{
|
|
pipe.m_Rasterizer.Scissors[s].Left = rs.Scissors[s].x;
|
|
pipe.m_Rasterizer.Scissors[s].Bottom = rs.Scissors[s].y;
|
|
pipe.m_Rasterizer.Scissors[s].Width = rs.Scissors[s].width;
|
|
pipe.m_Rasterizer.Scissors[s].Height = rs.Scissors[s].height;
|
|
pipe.m_Rasterizer.Scissors[s].Enabled = rs.Scissors[s].enabled;
|
|
}
|
|
|
|
int polygonOffsetEnableEnum;
|
|
switch (rs.PolygonMode)
|
|
{
|
|
default:
|
|
RDCWARN("Unexpected value for POLYGON_MODE %x", rs.PolygonMode);
|
|
case eGL_FILL:
|
|
pipe.m_Rasterizer.m_State.FillMode = eFill_Solid;
|
|
polygonOffsetEnableEnum = GLRenderState::eEnabled_PolyOffsetFill;
|
|
break;
|
|
case eGL_LINES:
|
|
pipe.m_Rasterizer.m_State.FillMode = eFill_Wireframe;
|
|
polygonOffsetEnableEnum = GLRenderState::eEnabled_PolyOffsetLine;
|
|
break;
|
|
case eGL_POINT:
|
|
pipe.m_Rasterizer.m_State.FillMode = eFill_Point;
|
|
polygonOffsetEnableEnum = GLRenderState::eEnabled_PolyOffsetPoint;
|
|
break;
|
|
}
|
|
if (rs.Enabled[polygonOffsetEnableEnum])
|
|
{
|
|
pipe.m_Rasterizer.m_State.DepthBias = rs.PolygonOffset[1];
|
|
pipe.m_Rasterizer.m_State.SlopeScaledDepthBias = rs.PolygonOffset[0];
|
|
pipe.m_Rasterizer.m_State.OffsetClamp = rs.PolygonOffset[2];
|
|
}
|
|
else
|
|
{
|
|
pipe.m_Rasterizer.m_State.DepthBias = 0.0f;
|
|
pipe.m_Rasterizer.m_State.SlopeScaledDepthBias = 0.0f;
|
|
pipe.m_Rasterizer.m_State.OffsetClamp = 0.0f;
|
|
}
|
|
|
|
if (rs.Enabled[GLRenderState::eEnabled_CullFace])
|
|
{
|
|
switch (rs.CullFace)
|
|
{
|
|
default:
|
|
RDCWARN("Unexpected value for CULL_FACE");
|
|
case eGL_BACK:
|
|
pipe.m_Rasterizer.m_State.CullMode = eCull_Back;
|
|
break;
|
|
case eGL_FRONT:
|
|
pipe.m_Rasterizer.m_State.CullMode = eCull_Front;
|
|
break;
|
|
case eGL_FRONT_AND_BACK:
|
|
pipe.m_Rasterizer.m_State.CullMode = eCull_FrontAndBack;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pipe.m_Rasterizer.m_State.CullMode = eCull_None;
|
|
}
|
|
|
|
RDCASSERT(rs.FrontFace == eGL_CCW || rs.FrontFace == eGL_CW);
|
|
pipe.m_Rasterizer.m_State.FrontCCW = rs.FrontFace == eGL_CCW;
|
|
pipe.m_Rasterizer.m_State.DepthClamp = rs.Enabled[GLRenderState::eEnabled_DepthClamp];
|
|
|
|
pipe.m_Rasterizer.m_State.MultisampleEnable = rs.Enabled[GLRenderState::eEnabled_Multisample];
|
|
pipe.m_Rasterizer.m_State.SampleShading = rs.Enabled[GLRenderState::eEnabled_SampleShading];
|
|
pipe.m_Rasterizer.m_State.SampleMask = rs.Enabled[GLRenderState::eEnabled_SampleMask];
|
|
pipe.m_Rasterizer.m_State.SampleMaskValue = rs.SampleMask[0]; // assume number of samples is less than 32
|
|
pipe.m_Rasterizer.m_State.SampleCoverage = rs.Enabled[GLRenderState::eEnabled_SampleCoverage];
|
|
pipe.m_Rasterizer.m_State.SampleCoverageInvert = rs.SampleCoverageInvert;
|
|
pipe.m_Rasterizer.m_State.SampleCoverageValue = rs.SampleCoverage;
|
|
pipe.m_Rasterizer.m_State.SampleAlphaToCoverage = rs.Enabled[GLRenderState::eEnabled_SampleAlphaToCoverage];
|
|
pipe.m_Rasterizer.m_State.SampleAlphaToOne = rs.Enabled[GLRenderState::eEnabled_SampleAlphaToOne];
|
|
pipe.m_Rasterizer.m_State.MinSampleShadingRate = rs.MinSampleShading;
|
|
|
|
pipe.m_Rasterizer.m_State.ProgrammablePointSize = rs.Enabled[rs.eEnabled_ProgramPointSize];
|
|
pipe.m_Rasterizer.m_State.PointSize = rs.PointSize;
|
|
pipe.m_Rasterizer.m_State.LineWidth = rs.LineWidth;
|
|
pipe.m_Rasterizer.m_State.PointFadeThreshold = rs.PointFadeThresholdSize;
|
|
pipe.m_Rasterizer.m_State.PointOriginUpperLeft = (rs.PointSpriteOrigin != eGL_LOWER_LEFT);
|
|
|
|
// depth and stencil states
|
|
|
|
pipe.m_DepthState.DepthEnable = rs.Enabled[GLRenderState::eEnabled_DepthTest];
|
|
pipe.m_DepthState.DepthWrites = rs.DepthWriteMask != 0;
|
|
pipe.m_DepthState.DepthFunc = ToStr::Get(rs.DepthFunc).substr(3);
|
|
|
|
pipe.m_DepthState.DepthBounds = rs.Enabled[GLRenderState::eEnabled_DepthBoundsEXT];
|
|
pipe.m_DepthState.NearBound = rs.DepthBounds.nearZ;
|
|
pipe.m_DepthState.FarBound = rs.DepthBounds.farZ;
|
|
|
|
pipe.m_StencilState.StencilEnable = rs.Enabled[GLRenderState::eEnabled_StencilTest];
|
|
pipe.m_StencilState.m_FrontFace.ValueMask = rs.StencilFront.valuemask;
|
|
pipe.m_StencilState.m_FrontFace.WriteMask = rs.StencilFront.writemask;
|
|
pipe.m_StencilState.m_FrontFace.Ref = rs.StencilFront.ref;
|
|
pipe.m_StencilState.m_FrontFace.Func = ToStr::Get(rs.StencilFront.func).substr(3);
|
|
pipe.m_StencilState.m_FrontFace.PassOp = ToStr::Get(rs.StencilFront.pass).substr(3);
|
|
pipe.m_StencilState.m_FrontFace.FailOp = ToStr::Get(rs.StencilFront.stencilFail).substr(3);
|
|
pipe.m_StencilState.m_FrontFace.DepthFailOp = ToStr::Get(rs.StencilFront.depthFail).substr(3);
|
|
pipe.m_StencilState.m_BackFace.ValueMask = rs.StencilBack.valuemask;
|
|
pipe.m_StencilState.m_BackFace.WriteMask = rs.StencilBack.writemask;
|
|
pipe.m_StencilState.m_BackFace.Ref = rs.StencilBack.ref;
|
|
pipe.m_StencilState.m_BackFace.Func = ToStr::Get(rs.StencilBack.func).substr(3);
|
|
pipe.m_StencilState.m_BackFace.PassOp = ToStr::Get(rs.StencilBack.pass).substr(3);
|
|
pipe.m_StencilState.m_BackFace.FailOp = ToStr::Get(rs.StencilBack.stencilFail).substr(3);
|
|
pipe.m_StencilState.m_BackFace.DepthFailOp = ToStr::Get(rs.StencilBack.depthFail).substr(3);
|
|
|
|
// Frame buffer
|
|
|
|
GLuint curDrawFBO = 0;
|
|
gl.glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&curDrawFBO);
|
|
GLuint curReadFBO = 0;
|
|
gl.glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, (GLint*)&curReadFBO);
|
|
|
|
GLint numCols = 8;
|
|
gl.glGetIntegerv(eGL_MAX_COLOR_ATTACHMENTS, &numCols);
|
|
|
|
bool rbCol[32] = { false };
|
|
bool rbDepth = false;
|
|
bool rbStencil = false;
|
|
GLuint curCol[32] = { 0 };
|
|
GLuint curDepth = 0;
|
|
GLuint curStencil = 0;
|
|
|
|
RDCASSERT(numCols <= 32);
|
|
|
|
// we should never bind the true default framebuffer - if the app did, we will have our fake bound
|
|
RDCASSERT(curDrawFBO != 0);
|
|
RDCASSERT(curReadFBO != 0);
|
|
|
|
{
|
|
GLenum type = eGL_TEXTURE;
|
|
for(GLint i=0; i < numCols; i++)
|
|
{
|
|
gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0+i), eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&curCol[i]);
|
|
gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0+i), eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint*)&type);
|
|
if(type == eGL_RENDERBUFFER) rbCol[i] = true;
|
|
}
|
|
|
|
gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&curDepth);
|
|
gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint*)&type);
|
|
if(type == eGL_RENDERBUFFER) rbDepth = true;
|
|
gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&curStencil);
|
|
gl.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint*)&type);
|
|
if(type == eGL_RENDERBUFFER) rbStencil = true;
|
|
|
|
pipe.m_FB.m_DrawFBO.Obj = rm->GetOriginalID(rm->GetID(FramebufferRes(ctx, curDrawFBO)));
|
|
create_array_uninit(pipe.m_FB.m_DrawFBO.Color, numCols);
|
|
for(GLint i=0; i < numCols; i++)
|
|
pipe.m_FB.m_DrawFBO.Color[i] = rm->GetOriginalID(rm->GetID(rbCol[i] ? RenderbufferRes(ctx, curCol[i]) : TextureRes(ctx, curCol[i])));
|
|
|
|
pipe.m_FB.m_DrawFBO.Depth = rm->GetOriginalID(rm->GetID(rbDepth ? RenderbufferRes(ctx, curDepth) : TextureRes(ctx, curDepth)));
|
|
pipe.m_FB.m_DrawFBO.Stencil = rm->GetOriginalID(rm->GetID(rbStencil ? RenderbufferRes(ctx, curStencil) : TextureRes(ctx, curStencil)));
|
|
|
|
create_array_uninit(pipe.m_FB.m_DrawFBO.DrawBuffers, numCols);
|
|
for(GLint i=0; i < numCols; i++)
|
|
{
|
|
GLenum b = eGL_NONE;
|
|
gl.glGetIntegerv(GLenum(eGL_DRAW_BUFFER0 + i), (GLint *)&b);
|
|
if(b >= eGL_COLOR_ATTACHMENT0 && b <= GLenum(eGL_COLOR_ATTACHMENT0+numCols))
|
|
pipe.m_FB.m_DrawFBO.DrawBuffers[i] = b-eGL_COLOR_ATTACHMENT0;
|
|
else
|
|
pipe.m_FB.m_DrawFBO.DrawBuffers[i] = -1;
|
|
}
|
|
|
|
pipe.m_FB.m_DrawFBO.ReadBuffer = -1;
|
|
}
|
|
|
|
{
|
|
GLenum type = eGL_TEXTURE;
|
|
for(GLint i=0; i < numCols; i++)
|
|
{
|
|
gl.glGetFramebufferAttachmentParameteriv(eGL_READ_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0+i), eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&curCol[i]);
|
|
gl.glGetFramebufferAttachmentParameteriv(eGL_READ_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0+i), eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint*)&type);
|
|
if(type == eGL_RENDERBUFFER) rbCol[i] = true;
|
|
}
|
|
|
|
gl.glGetFramebufferAttachmentParameteriv(eGL_READ_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&curDepth);
|
|
gl.glGetFramebufferAttachmentParameteriv(eGL_READ_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint*)&type);
|
|
if(type == eGL_RENDERBUFFER) rbDepth = true;
|
|
gl.glGetFramebufferAttachmentParameteriv(eGL_READ_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&curStencil);
|
|
gl.glGetFramebufferAttachmentParameteriv(eGL_READ_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint*)&type);
|
|
if(type == eGL_RENDERBUFFER) rbStencil = true;
|
|
|
|
pipe.m_FB.m_ReadFBO.Obj = rm->GetOriginalID(rm->GetID(FramebufferRes(ctx, curReadFBO)));
|
|
create_array_uninit(pipe.m_FB.m_ReadFBO.Color, numCols);
|
|
for(GLint i=0; i < numCols; i++)
|
|
pipe.m_FB.m_ReadFBO.Color[i] = rm->GetOriginalID(rm->GetID(rbCol[i] ? RenderbufferRes(ctx, curCol[i]) : TextureRes(ctx, curCol[i])));
|
|
|
|
pipe.m_FB.m_ReadFBO.Depth = rm->GetOriginalID(rm->GetID(rbDepth ? RenderbufferRes(ctx, curDepth) : TextureRes(ctx, curDepth)));
|
|
pipe.m_FB.m_ReadFBO.Stencil = rm->GetOriginalID(rm->GetID(rbStencil ? RenderbufferRes(ctx, curStencil) : TextureRes(ctx, curStencil)));
|
|
|
|
create_array_uninit(pipe.m_FB.m_ReadFBO.DrawBuffers, numCols);
|
|
for(GLint i=0; i < numCols; i++)
|
|
pipe.m_FB.m_ReadFBO.DrawBuffers[i] = -1;
|
|
|
|
GLenum b = eGL_NONE;
|
|
gl.glGetIntegerv(eGL_READ_BUFFER, (GLint *)&b);
|
|
if(b >= eGL_COLOR_ATTACHMENT0 && b <= GLenum(eGL_COLOR_ATTACHMENT0+numCols))
|
|
pipe.m_FB.m_DrawFBO.ReadBuffer = b-eGL_COLOR_ATTACHMENT0;
|
|
else
|
|
pipe.m_FB.m_DrawFBO.ReadBuffer = -1;
|
|
}
|
|
|
|
memcpy(pipe.m_FB.m_Blending.BlendFactor, rs.BlendColor, sizeof(rs.BlendColor));
|
|
|
|
pipe.m_FB.FramebufferSRGB = rs.Enabled[GLRenderState::eEnabled_FramebufferSRGB];
|
|
pipe.m_FB.Dither = rs.Enabled[GLRenderState::eEnabled_Dither];
|
|
|
|
RDCCOMPILE_ASSERT(ARRAY_COUNT(rs.Blends) == ARRAY_COUNT(rs.ColorMasks), "Color masks and blends mismatched");
|
|
create_array_uninit(pipe.m_FB.m_Blending.Blends, ARRAY_COUNT(rs.Blends));
|
|
for(size_t i=0; i < ARRAY_COUNT(rs.Blends); i++)
|
|
{
|
|
pipe.m_FB.m_Blending.Blends[i].Enabled = rs.Blends[i].Enabled;
|
|
pipe.m_FB.m_Blending.Blends[i].LogicOp = "";
|
|
if(rs.LogicOp != eGL_NONE && rs.LogicOp != eGL_COPY && rs.Enabled[GLRenderState::eEnabled_ColorLogicOp])
|
|
pipe.m_FB.m_Blending.Blends[i].LogicOp = ToStr::Get(rs.LogicOp).substr(3); // 3 == strlen("GL_")
|
|
|
|
pipe.m_FB.m_Blending.Blends[i].m_Blend.Source = BlendString(rs.Blends[i].SourceRGB);
|
|
pipe.m_FB.m_Blending.Blends[i].m_Blend.Destination = BlendString(rs.Blends[i].DestinationRGB);
|
|
pipe.m_FB.m_Blending.Blends[i].m_Blend.Operation = BlendString(rs.Blends[i].EquationRGB);
|
|
|
|
pipe.m_FB.m_Blending.Blends[i].m_AlphaBlend.Source = BlendString(rs.Blends[i].SourceAlpha);
|
|
pipe.m_FB.m_Blending.Blends[i].m_AlphaBlend.Destination = BlendString(rs.Blends[i].DestinationAlpha);
|
|
pipe.m_FB.m_Blending.Blends[i].m_AlphaBlend.Operation = BlendString(rs.Blends[i].EquationAlpha);
|
|
|
|
pipe.m_FB.m_Blending.Blends[i].WriteMask = 0;
|
|
if(rs.ColorMasks[i].red) pipe.m_FB.m_Blending.Blends[i].WriteMask |= 1;
|
|
if(rs.ColorMasks[i].green) pipe.m_FB.m_Blending.Blends[i].WriteMask |= 2;
|
|
if(rs.ColorMasks[i].blue) pipe.m_FB.m_Blending.Blends[i].WriteMask |= 4;
|
|
if(rs.ColorMasks[i].alpha) pipe.m_FB.m_Blending.Blends[i].WriteMask |= 8;
|
|
}
|
|
|
|
switch(rs.Hints.Derivatives)
|
|
{
|
|
default:
|
|
case eGL_DONT_CARE: pipe.m_Hints.Derivatives = eQuality_DontCare; break;
|
|
case eGL_NICEST: pipe.m_Hints.Derivatives = eQuality_Nicest; break;
|
|
case eGL_FASTEST: pipe.m_Hints.Derivatives = eQuality_Fastest; break;
|
|
}
|
|
|
|
switch(rs.Hints.LineSmooth)
|
|
{
|
|
default:
|
|
case eGL_DONT_CARE: pipe.m_Hints.LineSmooth = eQuality_DontCare; break;
|
|
case eGL_NICEST: pipe.m_Hints.LineSmooth = eQuality_Nicest; break;
|
|
case eGL_FASTEST: pipe.m_Hints.LineSmooth = eQuality_Fastest; break;
|
|
}
|
|
|
|
switch(rs.Hints.PolySmooth)
|
|
{
|
|
default:
|
|
case eGL_DONT_CARE: pipe.m_Hints.PolySmooth = eQuality_DontCare; break;
|
|
case eGL_NICEST: pipe.m_Hints.PolySmooth = eQuality_Nicest; break;
|
|
case eGL_FASTEST: pipe.m_Hints.PolySmooth = eQuality_Fastest; break;
|
|
}
|
|
|
|
switch(rs.Hints.TexCompression)
|
|
{
|
|
default:
|
|
case eGL_DONT_CARE: pipe.m_Hints.TexCompression = eQuality_DontCare; break;
|
|
case eGL_NICEST: pipe.m_Hints.TexCompression = eQuality_Nicest; break;
|
|
case eGL_FASTEST: pipe.m_Hints.TexCompression = eQuality_Fastest; break;
|
|
}
|
|
|
|
pipe.m_Hints.LineSmoothEnabled = rs.Enabled[GLRenderState::eEnabled_LineSmooth];
|
|
pipe.m_Hints.PolySmoothEnabled = rs.Enabled[GLRenderState::eEnabled_PolySmooth];
|
|
}
|
|
|
|
void GLReplay::FillCBufferValue(WrappedOpenGL &gl, GLuint prog, bool bufferBacked, bool rowMajor,
|
|
uint32_t offs, uint32_t matStride, const vector<byte> &data, ShaderVariable &outVar)
|
|
{
|
|
const byte *bufdata = data.empty() ? NULL : &data[offs];
|
|
size_t datasize = data.size() - offs;
|
|
if(offs > data.size()) datasize = 0;
|
|
|
|
if(bufferBacked)
|
|
{
|
|
size_t rangelen = outVar.rows*outVar.columns*sizeof(float);
|
|
|
|
if(outVar.rows > 1 && outVar.columns > 1)
|
|
{
|
|
uint32_t *dest = &outVar.value.uv[0];
|
|
|
|
uint32_t majorsize = outVar.columns;
|
|
uint32_t minorsize = outVar.rows;
|
|
|
|
if(rowMajor)
|
|
{
|
|
majorsize = outVar.rows;
|
|
minorsize = outVar.columns;
|
|
}
|
|
|
|
for(uint32_t c=0; c < majorsize; c++)
|
|
{
|
|
if(datasize > 0)
|
|
memcpy((byte *)dest, bufdata, RDCMIN(rangelen, minorsize*sizeof(float)));
|
|
|
|
datasize -= RDCMIN(datasize, (size_t)matStride);
|
|
bufdata += matStride;
|
|
dest += minorsize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(datasize > 0)
|
|
memcpy(&outVar.value.uv[0], bufdata, RDCMIN(rangelen, datasize));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(outVar.type)
|
|
{
|
|
case eVar_Float:
|
|
gl.glGetUniformfv(prog, offs, outVar.value.fv);
|
|
break;
|
|
case eVar_Int:
|
|
gl.glGetUniformiv(prog, offs, outVar.value.iv);
|
|
break;
|
|
case eVar_UInt:
|
|
gl.glGetUniformuiv(prog, offs, outVar.value.uv);
|
|
break;
|
|
case eVar_Double:
|
|
gl.glGetUniformdv(prog, offs, outVar.value.dv);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!rowMajor)
|
|
{
|
|
if(outVar.type != eVar_Double)
|
|
{
|
|
uint32_t uv[16];
|
|
memcpy(&uv[0], &outVar.value.uv[0], sizeof(uv));
|
|
|
|
for(uint32_t r=0; r < outVar.rows; r++)
|
|
for(uint32_t c=0; c < outVar.columns; c++)
|
|
outVar.value.uv[r*outVar.columns+c] = uv[c*outVar.rows+r];
|
|
}
|
|
else
|
|
{
|
|
double dv[16];
|
|
memcpy(&dv[0], &outVar.value.dv[0], sizeof(dv));
|
|
|
|
for(uint32_t r=0; r < outVar.rows; r++)
|
|
for(uint32_t c=0; c < outVar.columns; c++)
|
|
outVar.value.dv[r*outVar.columns+c] = dv[c*outVar.rows+r];
|
|
}
|
|
}
|
|
}
|
|
|
|
void GLReplay::FillCBufferVariables(WrappedOpenGL &gl, GLuint prog, bool bufferBacked, string prefix,
|
|
const rdctype::array<ShaderConstant> &variables, vector<ShaderVariable> &outvars,
|
|
const vector<byte> &data)
|
|
{
|
|
for(int32_t i=0; i < variables.count; i++)
|
|
{
|
|
auto desc = variables[i].type.descriptor;
|
|
|
|
ShaderVariable var;
|
|
var.name = variables[i].name.elems;
|
|
var.rows = desc.rows;
|
|
var.columns = desc.cols;
|
|
var.type = desc.type;
|
|
|
|
if(variables[i].type.members.count > 0)
|
|
{
|
|
if(desc.elements == 0)
|
|
{
|
|
vector<ShaderVariable> ov;
|
|
FillCBufferVariables(gl, prog, bufferBacked, prefix + var.name.elems + ".", variables[i].type.members, ov, data);
|
|
var.isStruct = true;
|
|
var.members = ov;
|
|
}
|
|
else
|
|
{
|
|
vector<ShaderVariable> arrelems;
|
|
for(uint32_t a=0; a < desc.elements; a++)
|
|
{
|
|
ShaderVariable arrEl = var;
|
|
arrEl.name = StringFormat::Fmt("%s[%u]", var.name.elems, a);
|
|
|
|
vector<ShaderVariable> ov;
|
|
FillCBufferVariables(gl, prog, bufferBacked, prefix + arrEl.name.elems + ".", variables[i].type.members, ov, data);
|
|
arrEl.members = ov;
|
|
|
|
arrEl.isStruct = true;
|
|
|
|
arrelems.push_back(arrEl);
|
|
}
|
|
var.members = arrelems;
|
|
var.isStruct = false;
|
|
var.rows = var.columns = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RDCEraseEl(var.value);
|
|
|
|
// need to query offset and strides as there's no way to know what layout was used
|
|
// (and if it's not an std layout it's implementation defined :( )
|
|
string fullname = prefix + var.name.elems;
|
|
|
|
GLuint idx = gl.glGetProgramResourceIndex(prog, eGL_UNIFORM, fullname.c_str());
|
|
|
|
if(idx == GL_INVALID_INDEX)
|
|
{
|
|
RDCERR("Can't find program resource index for %s", fullname.c_str());
|
|
}
|
|
else
|
|
{
|
|
GLenum props[] = { eGL_OFFSET, eGL_MATRIX_STRIDE, eGL_ARRAY_STRIDE, eGL_LOCATION };
|
|
GLint values[] = { 0, 0, 0, 0 };
|
|
|
|
gl.glGetProgramResourceiv(prog, eGL_UNIFORM, idx, ARRAY_COUNT(props), props, ARRAY_COUNT(props), NULL, values);
|
|
|
|
if(!bufferBacked)
|
|
{
|
|
values[0] = values[3];
|
|
values[2] = 1;
|
|
}
|
|
|
|
if(desc.elements == 0)
|
|
{
|
|
FillCBufferValue(gl, prog, bufferBacked, desc.rowMajorStorage ? true : false,
|
|
values[0], values[1], data, var);
|
|
}
|
|
else
|
|
{
|
|
vector<ShaderVariable> elems;
|
|
for(uint32_t a=0; a < desc.elements; a++)
|
|
{
|
|
ShaderVariable el = var;
|
|
el.name = StringFormat::Fmt("%s[%u]", var.name.elems, a);
|
|
|
|
FillCBufferValue(gl, prog, bufferBacked, desc.rowMajorStorage ? true : false,
|
|
values[0] + values[2] * a, values[1], data, el);
|
|
|
|
el.isStruct = false;
|
|
|
|
elems.push_back(el);
|
|
}
|
|
|
|
var.members = elems;
|
|
var.isStruct = false;
|
|
var.rows = var.columns = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
outvars.push_back(var);
|
|
}
|
|
}
|
|
|
|
void GLReplay::FillCBufferVariables(ResourceId shader, uint32_t cbufSlot, vector<ShaderVariable> &outvars, const vector<byte> &data)
|
|
{
|
|
WrappedOpenGL &gl = *m_pDriver;
|
|
|
|
MakeCurrentReplayContext(&m_ReplayCtx);
|
|
|
|
auto &shaderDetails = m_pDriver->m_Shaders[shader];
|
|
|
|
if((int32_t)cbufSlot >= shaderDetails.reflection.ConstantBlocks.count)
|
|
{
|
|
RDCERR("Requesting invalid constant block");
|
|
return;
|
|
}
|
|
|
|
GLuint curProg = 0;
|
|
gl.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint*)&curProg);
|
|
|
|
if(curProg == 0)
|
|
{
|
|
gl.glGetIntegerv(eGL_PROGRAM_PIPELINE_BINDING, (GLint*)&curProg);
|
|
|
|
if(curProg == 0)
|
|
{
|
|
RDCERR("No program or pipeline bound");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
ResourceId id = m_pDriver->GetResourceManager()->GetID(ProgramPipeRes(m_ReplayCtx.ctx, curProg));
|
|
auto &pipeDetails = m_pDriver->m_Pipelines[id];
|
|
|
|
size_t s = ShaderIdx(shaderDetails.type);
|
|
|
|
curProg = m_pDriver->GetResourceManager()->GetCurrentResource(pipeDetails.stagePrograms[s]).name;
|
|
}
|
|
}
|
|
|
|
auto cblock = shaderDetails.reflection.ConstantBlocks.elems[cbufSlot];
|
|
|
|
FillCBufferVariables(gl, curProg, cblock.bufferBacked ? true : false, "", cblock.variables, outvars, data);
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
void GLReplay::InitPostVSBuffers(uint32_t frameID, uint32_t eventID)
|
|
{
|
|
GLNOTIMP("GLReplay::InitPostVSBuffers");
|
|
}
|
|
|
|
vector<EventUsage> GLReplay::GetUsage(ResourceId id)
|
|
{
|
|
GLNOTIMP("GetUsage");
|
|
return vector<EventUsage>();
|
|
}
|
|
|
|
void GLReplay::SetContextFilter(ResourceId id, uint32_t firstDefEv, uint32_t lastDefEv)
|
|
{
|
|
RDCUNIMPLEMENTED("SetContextFilter");
|
|
}
|
|
|
|
void GLReplay::FreeTargetResource(ResourceId id)
|
|
{
|
|
RDCUNIMPLEMENTED("FreeTargetResource");
|
|
}
|
|
|
|
void GLReplay::FreeCustomShader(ResourceId id)
|
|
{
|
|
RDCUNIMPLEMENTED("FreeCustomShader");
|
|
}
|
|
|
|
MeshFormat GLReplay::GetPostVSBuffers(uint32_t frameID, uint32_t eventID, MeshDataStage stage)
|
|
{
|
|
MeshFormat ret;
|
|
RDCEraseEl(ret);
|
|
|
|
GLNOTIMP("GLReplay::GetPostVSBuffers");
|
|
|
|
return ret;
|
|
}
|
|
|
|
byte *GLReplay::GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip, bool resolve, bool forceRGBA8unorm, float blackPoint, float whitePoint, size_t &dataSize)
|
|
{
|
|
RDCUNIMPLEMENTED("GetTextureData");
|
|
return NULL;
|
|
}
|
|
|
|
void GLReplay::ReplaceResource(ResourceId from, ResourceId to)
|
|
{
|
|
RDCUNIMPLEMENTED("ReplaceResource");
|
|
}
|
|
|
|
void GLReplay::RemoveReplacement(ResourceId id)
|
|
{
|
|
RDCUNIMPLEMENTED("RemoveReplacement");
|
|
}
|
|
|
|
void GLReplay::TimeDrawcalls(rdctype::array<FetchDrawcall> &arr)
|
|
{
|
|
RDCUNIMPLEMENTED("TimeDrawcalls");
|
|
}
|
|
|
|
void GLReplay::BuildTargetShader(string source, string entry, const uint32_t compileFlags, ShaderStageType type, ResourceId *id, string *errors)
|
|
{
|
|
RDCUNIMPLEMENTED("BuildTargetShader");
|
|
}
|
|
|
|
void GLReplay::BuildCustomShader(string source, string entry, const uint32_t compileFlags, ShaderStageType type, ResourceId *id, string *errors)
|
|
{
|
|
RDCUNIMPLEMENTED("BuildCustomShader");
|
|
}
|
|
|
|
vector<PixelModification> GLReplay::PixelHistory(uint32_t frameID, vector<EventUsage> events, ResourceId target, uint32_t x, uint32_t y, uint32_t sampleIdx)
|
|
{
|
|
RDCUNIMPLEMENTED("GLReplay::PixelHistory");
|
|
return vector<PixelModification>();
|
|
}
|
|
|
|
ShaderDebugTrace GLReplay::DebugVertex(uint32_t frameID, uint32_t eventID, uint32_t vertid, uint32_t instid, uint32_t idx, uint32_t instOffset, uint32_t vertOffset)
|
|
{
|
|
RDCUNIMPLEMENTED("DebugVertex");
|
|
return ShaderDebugTrace();
|
|
}
|
|
|
|
ShaderDebugTrace GLReplay::DebugPixel(uint32_t frameID, uint32_t eventID, uint32_t x, uint32_t y, uint32_t sample, uint32_t primitive)
|
|
{
|
|
RDCUNIMPLEMENTED("DebugPixel");
|
|
return ShaderDebugTrace();
|
|
}
|
|
|
|
ShaderDebugTrace GLReplay::DebugThread(uint32_t frameID, uint32_t eventID, uint32_t groupid[3], uint32_t threadid[3])
|
|
{
|
|
RDCUNIMPLEMENTED("DebugThread");
|
|
return ShaderDebugTrace();
|
|
}
|
|
|
|
ResourceId GLReplay::ApplyCustomShader(ResourceId shader, ResourceId texid, uint32_t mip)
|
|
{
|
|
RDCUNIMPLEMENTED("ApplyCustomShader");
|
|
return ResourceId();
|
|
}
|
|
|
|
ResourceId GLReplay::CreateProxyTexture( FetchTexture templateTex )
|
|
{
|
|
RDCUNIMPLEMENTED("CreateProxyTexture");
|
|
return ResourceId();
|
|
}
|
|
|
|
void GLReplay::SetProxyTextureData(ResourceId texid, uint32_t arrayIdx, uint32_t mip, byte *data, size_t dataSize)
|
|
{
|
|
RDCUNIMPLEMENTED("SetProxyTextureData");
|
|
}
|
|
|
|
const GLHookSet &GetRealFunctions();
|
|
|
|
// defined in gl_replay_<platform>.cpp
|
|
ReplayCreateStatus GL_CreateReplayDevice(const char *logfile, IReplayDriver **driver);
|
|
|
|
static DriverRegistration GLDriverRegistration(RDC_OpenGL, "OpenGL", &GL_CreateReplayDevice);
|