mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-29 09:56:39 +00:00
4574 lines
146 KiB
C++
4574 lines
146 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2015-2018 Baldur Karlsson
|
|
* Copyright (c) 2014 Crytek
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
#include "../gl_driver.h"
|
|
#include "common/common.h"
|
|
#include "strings/string_utils.h"
|
|
|
|
void WrappedOpenGL::BindIndirectBuffer(GLsizeiptr bufLength)
|
|
{
|
|
if(m_IndirectBuffer == 0)
|
|
GL.glGenBuffers(1, &m_IndirectBuffer);
|
|
|
|
GL.glBindBuffer(eGL_DRAW_INDIRECT_BUFFER, m_IndirectBuffer);
|
|
|
|
if(m_IndirectBufferSize && bufLength <= m_IndirectBufferSize)
|
|
return;
|
|
|
|
GL.glBufferData(eGL_DRAW_INDIRECT_BUFFER, bufLength, NULL, eGL_DYNAMIC_DRAW);
|
|
}
|
|
|
|
enum GLbarrierbitfield
|
|
{
|
|
};
|
|
|
|
DECLARE_REFLECTION_ENUM(GLbarrierbitfield);
|
|
|
|
template <>
|
|
std::string DoStringise(const GLbarrierbitfield &el)
|
|
{
|
|
RDCCOMPILE_ASSERT(sizeof(GLbarrierbitfield) == sizeof(GLbitfield) &&
|
|
sizeof(GLbarrierbitfield) == sizeof(uint32_t),
|
|
"Fake bitfield enum must be uint32_t sized");
|
|
|
|
BEGIN_BITFIELD_STRINGISE(GLbarrierbitfield);
|
|
{
|
|
STRINGISE_BITFIELD_VALUE_NAMED((GLbarrierbitfield)GL_ALL_BARRIER_BITS, "GL_ALL_BARRIER_BITS");
|
|
|
|
STRINGISE_BITFIELD_BIT(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT);
|
|
STRINGISE_BITFIELD_BIT(GL_ELEMENT_ARRAY_BARRIER_BIT);
|
|
STRINGISE_BITFIELD_BIT(GL_UNIFORM_BARRIER_BIT);
|
|
STRINGISE_BITFIELD_BIT(GL_TEXTURE_FETCH_BARRIER_BIT);
|
|
STRINGISE_BITFIELD_BIT(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
|
|
STRINGISE_BITFIELD_BIT(GL_COMMAND_BARRIER_BIT);
|
|
STRINGISE_BITFIELD_BIT(GL_PIXEL_BUFFER_BARRIER_BIT);
|
|
STRINGISE_BITFIELD_BIT(GL_TEXTURE_UPDATE_BARRIER_BIT);
|
|
STRINGISE_BITFIELD_BIT(GL_BUFFER_UPDATE_BARRIER_BIT);
|
|
STRINGISE_BITFIELD_BIT(GL_FRAMEBUFFER_BARRIER_BIT);
|
|
STRINGISE_BITFIELD_BIT(GL_TRANSFORM_FEEDBACK_BARRIER_BIT);
|
|
STRINGISE_BITFIELD_BIT(GL_ATOMIC_COUNTER_BARRIER_BIT);
|
|
STRINGISE_BITFIELD_BIT(GL_SHADER_STORAGE_BARRIER_BIT);
|
|
}
|
|
END_BITFIELD_STRINGISE();
|
|
}
|
|
|
|
template <>
|
|
std::string DoStringise(const GLframebufferbitfield &el)
|
|
{
|
|
RDCCOMPILE_ASSERT(sizeof(GLframebufferbitfield) == sizeof(GLbitfield) &&
|
|
sizeof(GLframebufferbitfield) == sizeof(uint32_t),
|
|
"Fake bitfield enum must be uint32_t sized");
|
|
|
|
BEGIN_BITFIELD_STRINGISE(GLframebufferbitfield);
|
|
{
|
|
STRINGISE_BITFIELD_BIT(GL_COLOR_BUFFER_BIT);
|
|
STRINGISE_BITFIELD_BIT(GL_DEPTH_BUFFER_BIT);
|
|
STRINGISE_BITFIELD_BIT(GL_STENCIL_BUFFER_BIT);
|
|
}
|
|
END_BITFIELD_STRINGISE();
|
|
}
|
|
|
|
static constexpr uint32_t GetIdxSize(GLenum idxtype)
|
|
{
|
|
return (idxtype == eGL_UNSIGNED_BYTE ? 1 : (idxtype == eGL_UNSIGNED_SHORT ? 2 : 4));
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDispatchCompute(SerialiserType &ser, GLuint num_groups_x,
|
|
GLuint num_groups_y, GLuint num_groups_z)
|
|
{
|
|
SERIALISE_ELEMENT(num_groups_x);
|
|
SERIALISE_ELEMENT(num_groups_y);
|
|
SERIALISE_ELEMENT(num_groups_z);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glDispatchCompute(num_groups_x, num_groups_y, num_groups_z);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%u, %u, %u)", ToStr(gl_CurChunk).c_str(), num_groups_x,
|
|
num_groups_y, num_groups_z);
|
|
draw.flags |= DrawFlags::Dispatch;
|
|
|
|
draw.dispatchDimension[0] = num_groups_x;
|
|
draw.dispatchDimension[1] = num_groups_y;
|
|
draw.dispatchDimension[2] = num_groups_z;
|
|
|
|
if(num_groups_x == 0)
|
|
AddDebugMessage(MessageCategory::Execution, MessageSeverity::Medium,
|
|
MessageSource::IncorrectAPIUse,
|
|
"Dispatch call has num_groups_x=0. This will do nothing, which is unusual "
|
|
"for a non-indirect Dispatch. Did you mean X=1?");
|
|
if(num_groups_x == 0)
|
|
AddDebugMessage(MessageCategory::Execution, MessageSeverity::Medium,
|
|
MessageSource::IncorrectAPIUse,
|
|
"Dispatch call has num_groups_x=0. This will do nothing, which is unusual "
|
|
"for a non-indirect Dispatch. Did you mean Y=1?");
|
|
if(num_groups_z == 0)
|
|
AddDebugMessage(MessageCategory::Execution, MessageSeverity::Medium,
|
|
MessageSource::IncorrectAPIUse,
|
|
"Dispatch call has num_groups_z=0. This will do nothing, which is unusual "
|
|
"for a non-indirect Dispatch. Did you mean Z=1?");
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDispatchCompute(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDispatchCompute(num_groups_x, num_groups_y, num_groups_z));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDispatchCompute(ser, num_groups_x, num_groups_y, num_groups_z);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDispatchComputeGroupSizeARB(SerialiserType &ser,
|
|
GLuint num_groups_x, GLuint num_groups_y,
|
|
GLuint num_groups_z, GLuint group_size_x,
|
|
GLuint group_size_y, GLuint group_size_z)
|
|
{
|
|
SERIALISE_ELEMENT(num_groups_x);
|
|
SERIALISE_ELEMENT(num_groups_y);
|
|
SERIALISE_ELEMENT(num_groups_z);
|
|
SERIALISE_ELEMENT(group_size_x);
|
|
SERIALISE_ELEMENT(group_size_y);
|
|
SERIALISE_ELEMENT(group_size_z);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glDispatchComputeGroupSizeARB(num_groups_x, num_groups_y, num_groups_z, group_size_x,
|
|
group_size_y, group_size_z);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
DrawcallDescription draw;
|
|
draw.name =
|
|
StringFormat::Fmt("%s(%u, %u, %u, %u, %u, %u)", ToStr(gl_CurChunk).c_str(), num_groups_x,
|
|
num_groups_y, num_groups_z, group_size_x, group_size_y, group_size_z);
|
|
draw.flags |= DrawFlags::Dispatch;
|
|
|
|
draw.dispatchDimension[0] = num_groups_x;
|
|
draw.dispatchDimension[1] = num_groups_y;
|
|
draw.dispatchDimension[2] = num_groups_z;
|
|
draw.dispatchThreadsDimension[0] = group_size_x;
|
|
draw.dispatchThreadsDimension[1] = group_size_y;
|
|
draw.dispatchThreadsDimension[2] = group_size_z;
|
|
|
|
if(num_groups_x == 0)
|
|
AddDebugMessage(MessageCategory::Execution, MessageSeverity::Medium,
|
|
MessageSource::IncorrectAPIUse,
|
|
"Dispatch call has num_groups_x=0. This will do nothing, which is unusual "
|
|
"for a non-indirect Dispatch. Did you mean X=1?");
|
|
if(num_groups_y == 0)
|
|
AddDebugMessage(MessageCategory::Execution, MessageSeverity::Medium,
|
|
MessageSource::IncorrectAPIUse,
|
|
"Dispatch call has num_groups_y=0. This will do nothing, which is unusual "
|
|
"for a non-indirect Dispatch. Did you mean Y=1?");
|
|
if(num_groups_z == 0)
|
|
AddDebugMessage(MessageCategory::Execution, MessageSeverity::Medium,
|
|
MessageSource::IncorrectAPIUse,
|
|
"Dispatch call has num_groups_z=0. This will do nothing, which is unusual "
|
|
"for a non-indirect Dispatch. Did you mean Z=1?");
|
|
|
|
if(group_size_x == 0)
|
|
AddDebugMessage(MessageCategory::Execution, MessageSeverity::Medium,
|
|
MessageSource::IncorrectAPIUse,
|
|
"Dispatch call has group_size_x=0. This will do nothing, which is unusual "
|
|
"for a non-indirect Dispatch. Did you mean X=1?");
|
|
if(group_size_y == 0)
|
|
AddDebugMessage(MessageCategory::Execution, MessageSeverity::Medium,
|
|
MessageSource::IncorrectAPIUse,
|
|
"Dispatch call has group_size_y=0. This will do nothing, which is unusual "
|
|
"for a non-indirect Dispatch. Did you mean Y=1?");
|
|
if(group_size_z == 0)
|
|
AddDebugMessage(MessageCategory::Execution, MessageSeverity::Medium,
|
|
MessageSource::IncorrectAPIUse,
|
|
"Dispatch call has group_size_z=0. This will do nothing, which is unusual "
|
|
"for a non-indirect Dispatch. Did you mean Z=1?");
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDispatchComputeGroupSizeARB(GLuint num_groups_x, GLuint num_groups_y,
|
|
GLuint num_groups_z, GLuint group_size_x,
|
|
GLuint group_size_y, GLuint group_size_z)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDispatchComputeGroupSizeARB(num_groups_x, num_groups_y, num_groups_z,
|
|
group_size_x, group_size_y, group_size_z));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDispatchComputeGroupSizeARB(ser, num_groups_x, num_groups_y, num_groups_z,
|
|
group_size_x, group_size_y, group_size_z);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDispatchComputeIndirect(SerialiserType &ser, GLintptr indirect)
|
|
{
|
|
SERIALISE_ELEMENT_LOCAL(offset, (uint64_t)indirect);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glDispatchComputeIndirect((GLintptr)offset);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
uint32_t groupSizes[3];
|
|
GL.glGetBufferSubData(eGL_DISPATCH_INDIRECT_BUFFER, (GLintptr)offset, sizeof(uint32_t) * 3,
|
|
groupSizes);
|
|
|
|
AddEvent();
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(<%u, %u, %u>)", ToStr(gl_CurChunk).c_str(), groupSizes[0],
|
|
groupSizes[1], groupSizes[2]);
|
|
draw.flags |= DrawFlags::Dispatch | DrawFlags::Indirect;
|
|
|
|
draw.dispatchDimension[0] = groupSizes[0];
|
|
draw.dispatchDimension[1] = groupSizes[1];
|
|
draw.dispatchDimension[2] = groupSizes[2];
|
|
|
|
AddDrawcall(draw, true);
|
|
|
|
GLuint buf = 0;
|
|
GL.glGetIntegerv(eGL_DISPATCH_INDIRECT_BUFFER_BINDING, (GLint *)&buf);
|
|
|
|
m_ResourceUses[GetResourceManager()->GetID(BufferRes(GetCtx(), buf))].push_back(
|
|
EventUsage(m_CurEventID, ResourceUsage::Indirect));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDispatchComputeIndirect(GLintptr indirect)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDispatchComputeIndirect(indirect));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDispatchComputeIndirect(ser, indirect);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glMemoryBarrier(SerialiserType &ser, GLbitfield barriers)
|
|
{
|
|
SERIALISE_ELEMENT_TYPED(GLbarrierbitfield, barriers);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glMemoryBarrier(barriers);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glMemoryBarrier(GLbitfield barriers)
|
|
{
|
|
if(barriers & GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT)
|
|
{
|
|
// perform a forced flush of all persistent mapped buffers,
|
|
// coherent or not.
|
|
PersistentMapMemoryBarrier(m_PersistentMaps);
|
|
}
|
|
|
|
SERIALISE_TIME_CALL(GL.glMemoryBarrier(barriers));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glMemoryBarrier(ser, barriers);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glMemoryBarrierByRegion(SerialiserType &ser, GLbitfield barriers)
|
|
{
|
|
SERIALISE_ELEMENT_TYPED(GLbarrierbitfield, barriers);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glMemoryBarrierByRegion(barriers);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glMemoryBarrierByRegion(GLbitfield barriers)
|
|
{
|
|
if(barriers & GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT)
|
|
{
|
|
// perform a forced flush of all persistent mapped buffers,
|
|
// coherent or not.
|
|
PersistentMapMemoryBarrier(m_PersistentMaps);
|
|
}
|
|
|
|
SERIALISE_TIME_CALL(GL.glMemoryBarrierByRegion(barriers));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glMemoryBarrierByRegion(ser, barriers);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glTextureBarrier(SerialiserType &ser)
|
|
{
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glTextureBarrier();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glTextureBarrier()
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glTextureBarrier());
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glTextureBarrier(ser);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawTransformFeedback(SerialiserType &ser, GLenum mode,
|
|
GLuint xfbHandle)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT_LOCAL(xfb, FeedbackRes(GetCtx(), xfbHandle));
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glDrawTransformFeedback(mode, xfb.name);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
GLNOTIMP("Not fetching feedback object count for glDrawTransformFeedback() display");
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = ToStr(gl_CurChunk) + "(<?>)";
|
|
draw.numIndices = 1;
|
|
draw.numInstances = 1;
|
|
draw.indexOffset = 0;
|
|
draw.vertexOffset = 0;
|
|
draw.instanceOffset = 0;
|
|
|
|
draw.flags |= DrawFlags::Drawcall;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawTransformFeedback(GLenum mode, GLuint id)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDrawTransformFeedback(mode, id));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawTransformFeedback(ser, mode, id);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawTransformFeedbackInstanced(SerialiserType &ser, GLenum mode,
|
|
GLuint xfbHandle,
|
|
GLsizei instancecount)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT_LOCAL(xfb, FeedbackRes(GetCtx(), xfbHandle));
|
|
SERIALISE_ELEMENT(instancecount);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glDrawTransformFeedbackInstanced(mode, xfb.name, instancecount);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
GLNOTIMP("Not fetching feedback object count for glDrawTransformFeedbackInstanced() display");
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = ToStr(gl_CurChunk) + "(<?>)";
|
|
draw.numIndices = 1;
|
|
draw.numInstances = 1;
|
|
draw.indexOffset = 0;
|
|
draw.vertexOffset = 0;
|
|
draw.instanceOffset = 0;
|
|
|
|
draw.flags |= DrawFlags::Drawcall | DrawFlags::Instanced;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawTransformFeedbackInstanced(GLenum mode, GLuint id, GLsizei instancecount)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDrawTransformFeedbackInstanced(mode, id, instancecount));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawTransformFeedbackInstanced(ser, mode, id, instancecount);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawTransformFeedbackStream(SerialiserType &ser, GLenum mode,
|
|
GLuint xfbHandle, GLuint stream)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT_LOCAL(xfb, FeedbackRes(GetCtx(), xfbHandle));
|
|
SERIALISE_ELEMENT(stream);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glDrawTransformFeedbackStream(mode, xfb.name, stream);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
GLNOTIMP("Not fetching feedback object count for glDrawTransformFeedbackStream() display");
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = ToStr(gl_CurChunk) + "(<?>)";
|
|
draw.numIndices = 1;
|
|
draw.numInstances = 1;
|
|
draw.indexOffset = 0;
|
|
draw.vertexOffset = 0;
|
|
draw.instanceOffset = 0;
|
|
|
|
draw.flags |= DrawFlags::Drawcall;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawTransformFeedbackStream(GLenum mode, GLuint id, GLuint stream)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDrawTransformFeedbackStream(mode, id, stream));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawTransformFeedbackStream(ser, mode, id, stream);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawTransformFeedbackStreamInstanced(SerialiserType &ser, GLenum mode,
|
|
GLuint xfbHandle, GLuint stream,
|
|
GLsizei instancecount)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT_LOCAL(xfb, FeedbackRes(GetCtx(), xfbHandle));
|
|
SERIALISE_ELEMENT(stream);
|
|
SERIALISE_ELEMENT(instancecount);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glDrawTransformFeedbackStreamInstanced(mode, xfb.name, stream, instancecount);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
GLNOTIMP(
|
|
"Not fetching feedback object count for glDrawTransformFeedbackStreamInstanced() "
|
|
"display");
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = ToStr(gl_CurChunk) + "(<?>)";
|
|
draw.numIndices = 1;
|
|
draw.numInstances = 1;
|
|
draw.indexOffset = 0;
|
|
draw.vertexOffset = 0;
|
|
draw.instanceOffset = 0;
|
|
|
|
draw.flags |= DrawFlags::Drawcall | DrawFlags::Instanced;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawTransformFeedbackStreamInstanced(GLenum mode, GLuint id, GLuint stream,
|
|
GLsizei instancecount)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDrawTransformFeedbackStreamInstanced(mode, id, stream, instancecount));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawTransformFeedbackStreamInstanced(ser, mode, id, stream, instancecount);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawArrays(SerialiserType &ser, GLenum mode, GLint first,
|
|
GLsizei count)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT(first);
|
|
SERIALISE_ELEMENT(count);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glDrawArrays(mode, first, count);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%u)", ToStr(gl_CurChunk).c_str(), count);
|
|
draw.numIndices = count;
|
|
draw.numInstances = 1;
|
|
draw.indexOffset = 0;
|
|
draw.vertexOffset = first;
|
|
draw.instanceOffset = 0;
|
|
|
|
draw.flags |= DrawFlags::Drawcall;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
WrappedOpenGL::ClientMemoryData *WrappedOpenGL::CopyClientMemoryArrays(GLint first, GLsizei count,
|
|
GLenum indexType,
|
|
const void *&indices)
|
|
{
|
|
PUSH_CURRENT_CHUNK;
|
|
RDCASSERT(IsActiveCapturing(m_State));
|
|
ContextData &cd = GetCtxData();
|
|
|
|
GLint idxbuf = 0;
|
|
GLsizeiptr idxlen = 0;
|
|
const void *mmIndices = indices;
|
|
if(indexType != eGL_NONE)
|
|
{
|
|
idxlen = GLsizeiptr(count) * GetIdxSize(indexType);
|
|
|
|
GL.glGetIntegerv(eGL_ELEMENT_ARRAY_BUFFER_BINDING, &idxbuf);
|
|
if(idxbuf == 0)
|
|
{
|
|
// Bind and update fake index buffer, to draw from the 'immediate' index data
|
|
gl_CurChunk = GLChunk::glBindBuffer;
|
|
glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, cd.m_ClientMemoryIBO);
|
|
|
|
gl_CurChunk = GLChunk::glBufferData;
|
|
glBufferData(eGL_ELEMENT_ARRAY_BUFFER, idxlen, indices, eGL_STATIC_DRAW);
|
|
|
|
// Set offset to 0 - means we read data from start of our fake index buffer
|
|
indices = 0;
|
|
}
|
|
}
|
|
|
|
GLResourceRecord *varecord = cd.m_VertexArrayRecord;
|
|
if(varecord) // Early out if VAO bound, as VAOs are VBO-only.
|
|
return NULL;
|
|
|
|
ClientMemoryData *clientMemory = new ClientMemoryData;
|
|
GL.glGetIntegerv(eGL_ARRAY_BUFFER_BINDING, (GLint *)&clientMemory->prevArrayBufferBinding);
|
|
|
|
for(GLuint i = 0; i < ARRAY_COUNT(cd.m_ClientMemoryVBOs); i++)
|
|
{
|
|
GLint enabled = 0;
|
|
GL.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled);
|
|
if(!enabled)
|
|
continue;
|
|
|
|
// Check that the attrib is using client-memory.
|
|
GLuint buffer;
|
|
GL.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, (GLint *)&buffer);
|
|
if(buffer != 0)
|
|
continue;
|
|
|
|
if(indexType != eGL_NONE && first == -1)
|
|
{
|
|
bytebuf readbackIndices;
|
|
|
|
// First time we know we are using client-memory along with indices.
|
|
// Iterate over the indices to find the range of client memory to copy.
|
|
if(idxbuf != 0)
|
|
{
|
|
// If we were using a real index buffer, read it back to check its range.
|
|
readbackIndices.resize(idxlen);
|
|
GL.glGetBufferSubData(eGL_ELEMENT_ARRAY_BUFFER, (GLintptr)indices, idxlen,
|
|
readbackIndices.data());
|
|
mmIndices = readbackIndices.data();
|
|
}
|
|
|
|
size_t min = ~0u, max = 0;
|
|
GLsizei j;
|
|
switch(indexType)
|
|
{
|
|
case eGL_UNSIGNED_BYTE:
|
|
for(j = 0; j < count; j++)
|
|
{
|
|
min = RDCMIN(min, (size_t)((GLubyte *)mmIndices)[j]);
|
|
max = RDCMAX(max, (size_t)((GLubyte *)mmIndices)[j]);
|
|
}
|
|
break;
|
|
case eGL_UNSIGNED_SHORT:
|
|
for(j = 0; j < count; j++)
|
|
{
|
|
min = RDCMIN(min, (size_t)((GLushort *)mmIndices)[j]);
|
|
max = RDCMAX(max, (size_t)((GLushort *)mmIndices)[j]);
|
|
}
|
|
break;
|
|
case eGL_UNSIGNED_INT:
|
|
for(j = 0; j < count; j++)
|
|
{
|
|
min = RDCMIN(min, (size_t)((GLuint *)mmIndices)[j]);
|
|
max = RDCMAX(max, (size_t)((GLuint *)mmIndices)[j]);
|
|
}
|
|
break;
|
|
default:;
|
|
}
|
|
|
|
first = (GLint)min;
|
|
count = (GLint)(max - min + 1);
|
|
}
|
|
|
|
// App initially used client memory, so copy it into the temporary buffer.
|
|
ClientMemoryData::VertexAttrib attrib;
|
|
memset(&attrib, 0, sizeof(attrib));
|
|
attrib.index = i;
|
|
GL.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_SIZE, &attrib.size);
|
|
GL.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_TYPE, (GLint *)&attrib.type);
|
|
GL.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_NORMALIZED, (GLint *)&attrib.normalized);
|
|
GL.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_STRIDE, &attrib.stride);
|
|
GL.glGetVertexAttribPointerv(i, eGL_VERTEX_ATTRIB_ARRAY_POINTER, &attrib.pointer);
|
|
|
|
GLint totalStride = attrib.stride ? attrib.stride : (GLint)GLTypeSize(attrib.type) * attrib.size;
|
|
|
|
gl_CurChunk = GLChunk::glBindBuffer;
|
|
glBindBuffer(eGL_ARRAY_BUFFER, cd.m_ClientMemoryVBOs[i]);
|
|
|
|
// Copy all client memory, and the pointer becomes a zero offset.
|
|
gl_CurChunk = GLChunk::glBufferData;
|
|
glBufferData(eGL_ARRAY_BUFFER, (first + count) * totalStride, attrib.pointer, eGL_STATIC_DRAW);
|
|
|
|
gl_CurChunk = GLChunk::glVertexAttribPointer;
|
|
glVertexAttribPointer(attrib.index, attrib.size, attrib.type, attrib.normalized, attrib.stride,
|
|
NULL);
|
|
|
|
clientMemory->attribs.push_back(attrib);
|
|
}
|
|
|
|
return clientMemory;
|
|
}
|
|
|
|
void WrappedOpenGL::RestoreClientMemoryArrays(ClientMemoryData *clientMemoryArrays, GLenum indexType)
|
|
{
|
|
PUSH_CURRENT_CHUNK;
|
|
|
|
if(indexType != eGL_NONE)
|
|
{
|
|
ContextData &cd = GetCtxData();
|
|
GLuint idxbuf = 0;
|
|
GL.glGetIntegerv(eGL_ELEMENT_ARRAY_BUFFER_BINDING, (GLint *)&idxbuf);
|
|
if(idxbuf == cd.m_ClientMemoryIBO)
|
|
{
|
|
// Restore the zero buffer binding if we were using the fake index buffer.
|
|
gl_CurChunk = GLChunk::glBindBuffer;
|
|
glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, 0);
|
|
}
|
|
}
|
|
|
|
if(!clientMemoryArrays)
|
|
return;
|
|
|
|
// Restore the 0-buffer bindings and attrib pointers.
|
|
gl_CurChunk = GLChunk::glBindBuffer;
|
|
glBindBuffer(eGL_ARRAY_BUFFER, 0);
|
|
|
|
for(const ClientMemoryData::VertexAttrib &attrib : clientMemoryArrays->attribs)
|
|
{
|
|
gl_CurChunk = GLChunk::glVertexAttribPointer;
|
|
glVertexAttribPointer(attrib.index, attrib.size, attrib.type, attrib.normalized, attrib.stride,
|
|
attrib.pointer);
|
|
}
|
|
|
|
gl_CurChunk = GLChunk::glBindBuffer;
|
|
glBindBuffer(eGL_ARRAY_BUFFER, clientMemoryArrays->prevArrayBufferBinding);
|
|
|
|
delete clientMemoryArrays;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawArrays(GLenum mode, GLint first, GLsizei count)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDrawArrays(mode, first, count));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
const void *indices = NULL;
|
|
ClientMemoryData *clientMemory = CopyClientMemoryArrays(first, count, eGL_NONE, indices);
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawArrays(ser, mode, first, count);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
|
|
RestoreClientMemoryArrays(clientMemory, eGL_NONE);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawArraysIndirect(SerialiserType &ser, GLenum mode,
|
|
const void *indirect)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT_LOCAL(offset, (uint64_t)indirect);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glDrawArraysIndirect(mode, (const void *)offset);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
DrawArraysIndirectCommand params;
|
|
GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, (GLintptr)offset, sizeof(params), ¶ms);
|
|
|
|
AddEvent();
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%u, %u)", ToStr(gl_CurChunk).c_str(), params.count,
|
|
params.instanceCount);
|
|
draw.numIndices = params.count;
|
|
draw.numInstances = params.instanceCount;
|
|
draw.vertexOffset = params.first;
|
|
draw.instanceOffset = params.baseInstance;
|
|
|
|
draw.flags |= DrawFlags::Drawcall | DrawFlags::Instanced | DrawFlags::Indirect;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
|
|
AddDrawcall(draw, true);
|
|
|
|
GLuint buf = 0;
|
|
GL.glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING, (GLint *)&buf);
|
|
|
|
m_ResourceUses[GetResourceManager()->GetID(BufferRes(GetCtx(), buf))].push_back(
|
|
EventUsage(m_CurEventID, ResourceUsage::Indirect));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawArraysIndirect(GLenum mode, const void *indirect)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDrawArraysIndirect(mode, indirect));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawArraysIndirect(ser, mode, indirect);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawArraysInstanced(SerialiserType &ser, GLenum mode, GLint first,
|
|
GLsizei count, GLsizei instancecount)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT(first);
|
|
SERIALISE_ELEMENT(count);
|
|
SERIALISE_ELEMENT(instancecount);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glDrawArraysInstanced(mode, first, count, instancecount);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%u, %u)", ToStr(gl_CurChunk).c_str(), count, instancecount);
|
|
draw.numIndices = count;
|
|
draw.numInstances = instancecount;
|
|
draw.indexOffset = 0;
|
|
draw.vertexOffset = first;
|
|
draw.instanceOffset = 0;
|
|
|
|
draw.flags |= DrawFlags::Drawcall | DrawFlags::Instanced;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count,
|
|
GLsizei instancecount)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDrawArraysInstanced(mode, first, count, instancecount));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
const void *indices = NULL;
|
|
ClientMemoryData *clientMemory = CopyClientMemoryArrays(first, count, eGL_NONE, indices);
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawArraysInstanced(ser, mode, first, count, instancecount);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
|
|
RestoreClientMemoryArrays(clientMemory, eGL_NONE);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawArraysInstancedBaseInstance(SerialiserType &ser, GLenum mode,
|
|
GLint first, GLsizei count,
|
|
GLsizei instancecount,
|
|
GLuint baseinstance)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT(first);
|
|
SERIALISE_ELEMENT(count);
|
|
SERIALISE_ELEMENT(instancecount);
|
|
SERIALISE_ELEMENT(baseinstance);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glDrawArraysInstancedBaseInstance(mode, first, count, instancecount, baseinstance);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%u, %u)", ToStr(gl_CurChunk).c_str(), count, instancecount);
|
|
draw.numIndices = count;
|
|
draw.numInstances = instancecount;
|
|
draw.indexOffset = 0;
|
|
draw.vertexOffset = first;
|
|
draw.instanceOffset = baseinstance;
|
|
|
|
draw.flags |= DrawFlags::Drawcall | DrawFlags::Instanced;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count,
|
|
GLsizei instancecount, GLuint baseinstance)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(
|
|
GL.glDrawArraysInstancedBaseInstance(mode, first, count, instancecount, baseinstance));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
const void *indices = NULL;
|
|
ClientMemoryData *clientMemory = CopyClientMemoryArrays(first, count, eGL_NONE, indices);
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawArraysInstancedBaseInstance(ser, mode, first, count, instancecount, baseinstance);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
|
|
RestoreClientMemoryArrays(clientMemory, eGL_NONE);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
bool WrappedOpenGL::Check_preElements()
|
|
{
|
|
GLint idxbuf = 0;
|
|
GL.glGetIntegerv(eGL_ELEMENT_ARRAY_BUFFER_BINDING, &idxbuf);
|
|
|
|
if(idxbuf == 0)
|
|
{
|
|
AddDebugMessage(MessageCategory::Undefined, MessageSeverity::High,
|
|
MessageSource::IncorrectAPIUse, "No index buffer bound at indexed draw!.");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawElements(SerialiserType &ser, GLenum mode, GLsizei count,
|
|
GLenum type, const void *indicesPtr)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT(count);
|
|
SERIALISE_ELEMENT(type);
|
|
SERIALISE_ELEMENT_LOCAL(indices, (uint64_t)indicesPtr);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(Check_preElements())
|
|
GL.glDrawElements(mode, count, type, (const void *)indices);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
uint32_t IdxSize = GetIdxSize(type);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%u)", ToStr(gl_CurChunk).c_str(), count);
|
|
draw.numIndices = count;
|
|
draw.numInstances = 1;
|
|
draw.indexOffset = uint32_t(indices) / IdxSize;
|
|
draw.vertexOffset = 0;
|
|
draw.instanceOffset = 0;
|
|
|
|
draw.flags |= DrawFlags::Drawcall | DrawFlags::Indexed;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
draw.indexByteWidth = IdxSize;
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDrawElements(mode, count, type, indices));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
ClientMemoryData *clientMemory = CopyClientMemoryArrays(-1, count, type, indices);
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawElements(ser, mode, count, type, indices);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
|
|
RestoreClientMemoryArrays(clientMemory, type);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawElementsIndirect(SerialiserType &ser, GLenum mode, GLenum type,
|
|
const void *indirect)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT(type);
|
|
SERIALISE_ELEMENT_LOCAL(offset, (uint64_t)indirect);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glDrawElementsIndirect(mode, type, (const void *)offset);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
DrawElementsIndirectCommand params;
|
|
GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, (GLintptr)offset, sizeof(params), ¶ms);
|
|
|
|
AddEvent();
|
|
|
|
uint32_t IdxSize = GetIdxSize(type);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(<%u, %u>)", ToStr(gl_CurChunk).c_str(), params.count,
|
|
params.instanceCount);
|
|
draw.numIndices = params.count;
|
|
draw.numInstances = params.instanceCount;
|
|
draw.indexOffset = params.firstIndex;
|
|
draw.baseVertex = params.baseVertex;
|
|
draw.instanceOffset = params.baseInstance;
|
|
|
|
draw.flags |=
|
|
DrawFlags::Drawcall | DrawFlags::Indexed | DrawFlags::Instanced | DrawFlags::Indirect;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
draw.indexByteWidth = IdxSize;
|
|
|
|
AddDrawcall(draw, true);
|
|
|
|
GLuint buf = 0;
|
|
GL.glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING, (GLint *)&buf);
|
|
|
|
m_ResourceUses[GetResourceManager()->GetID(BufferRes(GetCtx(), buf))].push_back(
|
|
EventUsage(m_CurEventID, ResourceUsage::Indirect));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawElementsIndirect(GLenum mode, GLenum type, const void *indirect)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDrawElementsIndirect(mode, type, indirect));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawElementsIndirect(ser, mode, type, indirect);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawRangeElements(SerialiserType &ser, GLenum mode, GLuint start,
|
|
GLuint end, GLsizei count, GLenum type,
|
|
const void *indicesPtr)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT(start);
|
|
SERIALISE_ELEMENT(end);
|
|
SERIALISE_ELEMENT(count);
|
|
SERIALISE_ELEMENT(type);
|
|
SERIALISE_ELEMENT_LOCAL(indices, (uint64_t)indicesPtr);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(Check_preElements())
|
|
GL.glDrawRangeElements(mode, start, end, count, type, (const void *)indices);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
uint32_t IdxSize = GetIdxSize(type);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%u)", ToStr(gl_CurChunk).c_str(), count);
|
|
draw.numIndices = count;
|
|
draw.numInstances = 1;
|
|
draw.indexOffset = uint32_t(indices) / IdxSize;
|
|
draw.vertexOffset = 0;
|
|
draw.instanceOffset = 0;
|
|
|
|
draw.flags |= DrawFlags::Drawcall | DrawFlags::Indexed;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
draw.indexByteWidth = IdxSize;
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count,
|
|
GLenum type, const void *indices)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDrawRangeElements(mode, start, end, count, type, indices));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
ClientMemoryData *clientMemory = CopyClientMemoryArrays(-1, count, type, indices);
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawRangeElements(ser, mode, start, end, count, type, indices);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
|
|
RestoreClientMemoryArrays(clientMemory, type);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawRangeElementsBaseVertex(SerialiserType &ser, GLenum mode,
|
|
GLuint start, GLuint end, GLsizei count,
|
|
GLenum type, const void *indicesPtr,
|
|
GLint basevertex)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT(start);
|
|
SERIALISE_ELEMENT(end);
|
|
SERIALISE_ELEMENT(count);
|
|
SERIALISE_ELEMENT(type);
|
|
SERIALISE_ELEMENT_LOCAL(indices, (uint64_t)indicesPtr);
|
|
SERIALISE_ELEMENT(basevertex);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(Check_preElements())
|
|
GL.glDrawRangeElementsBaseVertex(mode, start, end, count, type, (const void *)indices,
|
|
basevertex);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
uint32_t IdxSize = GetIdxSize(type);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%u)", ToStr(gl_CurChunk).c_str(), count);
|
|
draw.numIndices = count;
|
|
draw.numInstances = 1;
|
|
draw.indexOffset = uint32_t(indices) / IdxSize;
|
|
draw.baseVertex = basevertex;
|
|
draw.instanceOffset = 0;
|
|
|
|
draw.flags |= DrawFlags::Drawcall | DrawFlags::Indexed;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
draw.indexByteWidth = IdxSize;
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end,
|
|
GLsizei count, GLenum type, const void *indices,
|
|
GLint basevertex)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(
|
|
GL.glDrawRangeElementsBaseVertex(mode, start, end, count, type, indices, basevertex));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
ClientMemoryData *clientMemory = CopyClientMemoryArrays(-1, count, type, indices);
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawRangeElementsBaseVertex(ser, mode, start, end, count, type, indices, basevertex);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
|
|
RestoreClientMemoryArrays(clientMemory, type);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawElementsBaseVertex(SerialiserType &ser, GLenum mode,
|
|
GLsizei count, GLenum type,
|
|
const void *indicesPtr, GLint basevertex)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT(count);
|
|
SERIALISE_ELEMENT(type);
|
|
SERIALISE_ELEMENT_LOCAL(indices, (uint64_t)indicesPtr);
|
|
SERIALISE_ELEMENT(basevertex);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(Check_preElements())
|
|
GL.glDrawElementsBaseVertex(mode, count, type, (const void *)indices, basevertex);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
uint32_t IdxSize = GetIdxSize(type);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%u)", ToStr(gl_CurChunk).c_str(), count);
|
|
draw.numIndices = count;
|
|
draw.numInstances = 1;
|
|
draw.indexOffset = uint32_t(indices) / IdxSize;
|
|
draw.baseVertex = basevertex;
|
|
draw.instanceOffset = 0;
|
|
|
|
draw.flags |= DrawFlags::Drawcall | DrawFlags::Indexed;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
draw.indexByteWidth = IdxSize;
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type,
|
|
const void *indices, GLint basevertex)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDrawElementsBaseVertex(mode, count, type, indices, basevertex));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
ClientMemoryData *clientMemory = CopyClientMemoryArrays(-1, count, type, indices);
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawElementsBaseVertex(ser, mode, count, type, indices, basevertex);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
|
|
RestoreClientMemoryArrays(clientMemory, type);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawElementsInstanced(SerialiserType &ser, GLenum mode,
|
|
GLsizei count, GLenum type,
|
|
const void *indicesPtr, GLsizei instancecount)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT(count);
|
|
SERIALISE_ELEMENT(type);
|
|
SERIALISE_ELEMENT_LOCAL(indices, (uint64_t)indicesPtr);
|
|
SERIALISE_ELEMENT(instancecount);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(Check_preElements())
|
|
GL.glDrawElementsInstanced(mode, count, type, (const void *)indices, instancecount);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
uint32_t IdxSize = GetIdxSize(type);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%u, %u)", ToStr(gl_CurChunk).c_str(), count, instancecount);
|
|
draw.numIndices = count;
|
|
draw.numInstances = instancecount;
|
|
draw.indexOffset = uint32_t(indices) / IdxSize;
|
|
draw.vertexOffset = 0;
|
|
draw.instanceOffset = 0;
|
|
|
|
draw.flags |= DrawFlags::Drawcall | DrawFlags::Indexed | DrawFlags::Instanced;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
draw.indexByteWidth = IdxSize;
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
|
|
const void *indices, GLsizei instancecount)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDrawElementsInstanced(mode, count, type, indices, instancecount));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
ClientMemoryData *clientMemory = CopyClientMemoryArrays(-1, count, type, indices);
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawElementsInstanced(ser, mode, count, type, indices, instancecount);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
|
|
RestoreClientMemoryArrays(clientMemory, type);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawElementsInstancedBaseInstance(SerialiserType &ser, GLenum mode,
|
|
GLsizei count, GLenum type,
|
|
const void *indicesPtr,
|
|
GLsizei instancecount,
|
|
GLuint baseinstance)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT(count);
|
|
SERIALISE_ELEMENT(type);
|
|
SERIALISE_ELEMENT_LOCAL(indices, (uint64_t)indicesPtr);
|
|
SERIALISE_ELEMENT(instancecount);
|
|
SERIALISE_ELEMENT(baseinstance);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(Check_preElements())
|
|
GL.glDrawElementsInstancedBaseInstance(mode, count, type, (const void *)indices,
|
|
instancecount, baseinstance);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
uint32_t IdxSize = GetIdxSize(type);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%u, %u)", ToStr(gl_CurChunk).c_str(), count, instancecount);
|
|
draw.numIndices = count;
|
|
draw.numInstances = instancecount;
|
|
draw.indexOffset = uint32_t(indices) / IdxSize;
|
|
draw.vertexOffset = 0;
|
|
draw.instanceOffset = baseinstance;
|
|
|
|
draw.flags |= DrawFlags::Drawcall | DrawFlags::Instanced | DrawFlags::Indexed;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
draw.indexByteWidth = IdxSize;
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type,
|
|
const void *indices, GLsizei instancecount,
|
|
GLuint baseinstance)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDrawElementsInstancedBaseInstance(mode, count, type, indices,
|
|
instancecount, baseinstance));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
ClientMemoryData *clientMemory = CopyClientMemoryArrays(-1, count, type, indices);
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawElementsInstancedBaseInstance(ser, mode, count, type, indices, instancecount,
|
|
baseinstance);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
|
|
RestoreClientMemoryArrays(clientMemory, type);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawElementsInstancedBaseVertex(SerialiserType &ser, GLenum mode,
|
|
GLsizei count, GLenum type,
|
|
const void *indicesPtr,
|
|
GLsizei instancecount,
|
|
GLint basevertex)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT(count);
|
|
SERIALISE_ELEMENT(type);
|
|
SERIALISE_ELEMENT_LOCAL(indices, (uint64_t)indicesPtr);
|
|
SERIALISE_ELEMENT(instancecount);
|
|
SERIALISE_ELEMENT(basevertex);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(Check_preElements())
|
|
GL.glDrawElementsInstancedBaseVertex(mode, count, type, (const void *)indices, instancecount,
|
|
basevertex);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
uint32_t IdxSize = GetIdxSize(type);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%u, %u)", ToStr(gl_CurChunk).c_str(), count, instancecount);
|
|
draw.numIndices = count;
|
|
draw.numInstances = instancecount;
|
|
draw.indexOffset = uint32_t(indices) / IdxSize;
|
|
draw.baseVertex = basevertex;
|
|
draw.instanceOffset = 0;
|
|
|
|
draw.flags |= DrawFlags::Drawcall | DrawFlags::Instanced | DrawFlags::Indexed;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
draw.indexByteWidth = IdxSize;
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type,
|
|
const void *indices, GLsizei instancecount,
|
|
GLint basevertex)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(
|
|
GL.glDrawElementsInstancedBaseVertex(mode, count, type, indices, instancecount, basevertex));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
ClientMemoryData *clientMemory = CopyClientMemoryArrays(-1, count, type, indices);
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawElementsInstancedBaseVertex(ser, mode, count, type, indices, instancecount,
|
|
basevertex);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
|
|
RestoreClientMemoryArrays(clientMemory, type);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glDrawElementsInstancedBaseVertexBaseInstance(
|
|
SerialiserType &ser, GLenum mode, GLsizei count, GLenum type, const void *indicesPtr,
|
|
GLsizei instancecount, GLint basevertex, GLuint baseinstance)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT(count);
|
|
SERIALISE_ELEMENT(type);
|
|
SERIALISE_ELEMENT_LOCAL(indices, (uint64_t)indicesPtr);
|
|
SERIALISE_ELEMENT(instancecount);
|
|
SERIALISE_ELEMENT(basevertex);
|
|
SERIALISE_ELEMENT(baseinstance);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(Check_preElements())
|
|
GL.glDrawElementsInstancedBaseVertexBaseInstance(mode, count, type, (const void *)indices,
|
|
instancecount, basevertex, baseinstance);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
uint32_t IdxSize = GetIdxSize(type);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%u, %u)", ToStr(gl_CurChunk).c_str(), count, instancecount);
|
|
draw.numIndices = count;
|
|
draw.numInstances = instancecount;
|
|
draw.indexOffset = uint32_t(indices) / IdxSize;
|
|
draw.baseVertex = basevertex;
|
|
draw.instanceOffset = baseinstance;
|
|
|
|
draw.flags |= DrawFlags::Drawcall | DrawFlags::Instanced | DrawFlags::Indexed;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
draw.indexByteWidth = IdxSize;
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glDrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count,
|
|
GLenum type, const void *indices,
|
|
GLsizei instancecount,
|
|
GLint basevertex,
|
|
GLuint baseinstance)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glDrawElementsInstancedBaseVertexBaseInstance(
|
|
mode, count, type, indices, instancecount, basevertex, baseinstance));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
ClientMemoryData *clientMemory = CopyClientMemoryArrays(-1, count, type, indices);
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glDrawElementsInstancedBaseVertexBaseInstance(
|
|
ser, mode, count, type, indices, instancecount, basevertex, baseinstance);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
|
|
RestoreClientMemoryArrays(clientMemory, type);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glMultiDrawArrays(SerialiserType &ser, GLenum mode, const GLint *first,
|
|
const GLsizei *count, GLsizei drawcount)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT_ARRAY(first, drawcount);
|
|
SERIALISE_ELEMENT_ARRAY(count, drawcount);
|
|
SERIALISE_ELEMENT(drawcount);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(IsLoading(m_State))
|
|
{
|
|
GL.glMultiDrawArrays(mode, first, count, drawcount);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%i)", ToStr(gl_CurChunk).c_str(), drawcount);
|
|
draw.flags |= DrawFlags::MultiDraw;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
|
|
AddDrawcall(draw, false);
|
|
|
|
m_DrawcallStack.push_back(&m_DrawcallStack.back()->children.back());
|
|
|
|
for(GLsizei i = 0; i < drawcount; i++)
|
|
{
|
|
m_CurEventID++;
|
|
|
|
DrawcallDescription multidraw;
|
|
multidraw.drawIndex = i;
|
|
multidraw.numIndices = count[i];
|
|
multidraw.vertexOffset = first[i];
|
|
|
|
multidraw.name =
|
|
StringFormat::Fmt("%s[%i](%u)", ToStr(gl_CurChunk).c_str(), i, multidraw.numIndices);
|
|
|
|
multidraw.flags |= DrawFlags::Drawcall;
|
|
|
|
multidraw.topology = MakePrimitiveTopology(mode);
|
|
|
|
AddEvent();
|
|
AddDrawcall(multidraw, true);
|
|
}
|
|
|
|
m_DrawcallStack.pop_back();
|
|
}
|
|
else if(IsActiveReplaying(m_State))
|
|
{
|
|
size_t i = 0;
|
|
for(; i < m_Events.size(); i++)
|
|
{
|
|
if(m_Events[i].eventId >= m_CurEventID)
|
|
break;
|
|
}
|
|
|
|
while(i > 1 && m_Events[i - 1].fileOffset == m_Events[i].fileOffset)
|
|
i--;
|
|
|
|
uint32_t baseEventID = m_Events[i].eventId;
|
|
|
|
if(m_LastEventID < baseEventID)
|
|
{
|
|
// To add the multidraw, we made an event N that is the 'parent' marker, then
|
|
// N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected
|
|
// then we'll replay up to N but not N+1, so just do nothing - we DON'T want to draw
|
|
// the first sub-draw in that range.
|
|
}
|
|
else if(m_FirstEventID <= baseEventID && m_LastEventID >= baseEventID)
|
|
{
|
|
// if we're replaying part-way into a multidraw, we can replay the first part 'easily'
|
|
// by just reducing the drawcount parameter to however many we want to replay. This only
|
|
// works if we're replaying from the first multidraw to the nth (n less than drawcount)
|
|
GL.glMultiDrawArrays(mode, first, count,
|
|
RDCMIN((uint32_t)drawcount, m_LastEventID - baseEventID + 1));
|
|
}
|
|
else
|
|
{
|
|
// otherwise we do the 'hard' case, draw only one multidraw
|
|
// note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or
|
|
// a single draw.
|
|
//
|
|
// We also need to use the original glMultiDraw command so that gl_DrawID is faithful. In
|
|
// order to preserve the draw index we write a custom multidraw that specifies count == 0
|
|
// for all previous draws.
|
|
RDCASSERT(m_LastEventID == m_FirstEventID);
|
|
|
|
uint32_t drawidx = (m_LastEventID - baseEventID);
|
|
|
|
// zero out the count for all previous draws. This won't be used again so we can safely
|
|
// write over the serialised array.
|
|
GLsizei *modcount = (GLsizei *)count;
|
|
for(uint32_t d = 0; d < drawidx; d++)
|
|
modcount[d] = 0;
|
|
|
|
GL.glMultiDrawArrays(mode, first, count, drawidx + 1);
|
|
}
|
|
|
|
m_CurEventID += (uint32_t)drawcount;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glMultiDrawArrays(GLenum mode, const GLint *first, const GLsizei *count,
|
|
GLsizei drawcount)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glMultiDrawArrays(mode, first, count, drawcount));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glMultiDrawArrays(ser, mode, first, count, drawcount);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glMultiDrawElements(SerialiserType &ser, GLenum mode,
|
|
const GLsizei *count, GLenum type,
|
|
const void *const *indicesPtr, GLsizei drawcount)
|
|
{
|
|
// need to serialise the array by hand since the pointers are really offsets :(.
|
|
std::vector<uint64_t> indices;
|
|
if(ser.IsWriting())
|
|
{
|
|
indices.reserve(drawcount);
|
|
for(GLsizei i = 0; i < drawcount; i++)
|
|
indices.push_back((uint64_t)indicesPtr[i]);
|
|
}
|
|
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT_ARRAY(count, drawcount);
|
|
SERIALISE_ELEMENT(type);
|
|
SERIALISE_ELEMENT(indices);
|
|
SERIALISE_ELEMENT(drawcount);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
std::vector<const void *> inds;
|
|
inds.reserve(drawcount);
|
|
for(GLsizei i = 0; i < drawcount; i++)
|
|
inds.push_back((const void *)indices[i]);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
GL.glMultiDrawElements(mode, count, type, inds.data(), drawcount);
|
|
|
|
uint32_t IdxSize = GetIdxSize(type);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%i)", ToStr(gl_CurChunk).c_str(), drawcount);
|
|
|
|
draw.flags |= DrawFlags::MultiDraw;
|
|
draw.indexByteWidth = IdxSize;
|
|
draw.numIndices = 0;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
|
|
AddDrawcall(draw, false);
|
|
|
|
m_DrawcallStack.push_back(&m_DrawcallStack.back()->children.back());
|
|
|
|
for(GLsizei i = 0; i < drawcount; i++)
|
|
{
|
|
m_CurEventID++;
|
|
|
|
DrawcallDescription multidraw;
|
|
multidraw.drawIndex = i;
|
|
multidraw.numIndices = count[i];
|
|
multidraw.indexOffset = (uint32_t)(indices[i] & 0xFFFFFFFF);
|
|
multidraw.indexByteWidth = IdxSize;
|
|
|
|
multidraw.indexOffset /= IdxSize;
|
|
|
|
multidraw.name =
|
|
StringFormat::Fmt("%s[%i](%u)", ToStr(gl_CurChunk).c_str(), i, multidraw.numIndices);
|
|
|
|
multidraw.flags |= DrawFlags::Drawcall | DrawFlags::Indexed;
|
|
|
|
multidraw.topology = MakePrimitiveTopology(mode);
|
|
|
|
AddEvent();
|
|
AddDrawcall(multidraw, true);
|
|
}
|
|
|
|
m_DrawcallStack.pop_back();
|
|
}
|
|
else if(IsActiveReplaying(m_State))
|
|
{
|
|
size_t i = 0;
|
|
for(; i < m_Events.size(); i++)
|
|
{
|
|
if(m_Events[i].eventId >= m_CurEventID)
|
|
break;
|
|
}
|
|
|
|
while(i > 1 && m_Events[i - 1].fileOffset == m_Events[i].fileOffset)
|
|
i--;
|
|
|
|
uint32_t baseEventID = m_Events[i].eventId;
|
|
|
|
if(m_LastEventID < baseEventID)
|
|
{
|
|
// To add the multidraw, we made an event N that is the 'parent' marker, then
|
|
// N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected
|
|
// then we'll replay up to N but not N+1, so just do nothing - we DON'T want to draw
|
|
// the first sub-draw in that range.
|
|
}
|
|
else if(m_FirstEventID <= baseEventID && m_LastEventID >= baseEventID)
|
|
{
|
|
// if we're replaying part-way into a multidraw, we can replay the first part 'easily'
|
|
// by just reducing the Count parameter to however many we want to replay. This only
|
|
// works if we're replaying from the first multidraw to the nth (n less than Count)
|
|
GL.glMultiDrawElements(mode, count, type, inds.data(),
|
|
RDCMIN((uint32_t)drawcount, m_LastEventID - baseEventID + 1));
|
|
}
|
|
else
|
|
{
|
|
// otherwise we do the 'hard' case, draw only one multidraw
|
|
// note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or
|
|
// a single draw.
|
|
//
|
|
// We also need to use the original glMultiDraw command so that gl_DrawID is faithful. In
|
|
// order to preserve the draw index we write a custom multidraw that specifies count == 0
|
|
// for all previous draws.
|
|
RDCASSERT(m_LastEventID == m_FirstEventID);
|
|
|
|
uint32_t drawidx = (m_LastEventID - baseEventID);
|
|
|
|
// zero out the count for all previous draws. This won't be used again so we can safely
|
|
// write over the serialised array.
|
|
GLsizei *modcount = (GLsizei *)count;
|
|
for(uint32_t d = 0; d < drawidx; d++)
|
|
modcount[d] = 0;
|
|
|
|
GL.glMultiDrawElements(mode, count, type, inds.data(), drawidx + 1);
|
|
}
|
|
|
|
m_CurEventID += (uint32_t)drawcount;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glMultiDrawElements(GLenum mode, const GLsizei *count, GLenum type,
|
|
const void *const *indices, GLsizei drawcount)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glMultiDrawElements(mode, count, type, indices, drawcount));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glMultiDrawElements(ser, mode, count, type, indices, drawcount);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glMultiDrawElementsBaseVertex(SerialiserType &ser, GLenum mode,
|
|
const GLsizei *count, GLenum type,
|
|
const void *const *indicesPtr,
|
|
GLsizei drawcount,
|
|
const GLint *basevertex)
|
|
{
|
|
// need to serialise the array by hand since the pointers are really offsets :(.
|
|
std::vector<uint64_t> indices;
|
|
if(ser.IsWriting())
|
|
{
|
|
indices.reserve(drawcount);
|
|
for(GLsizei i = 0; i < drawcount; i++)
|
|
indices.push_back((uint64_t)indicesPtr[i]);
|
|
}
|
|
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT_ARRAY(count, drawcount);
|
|
SERIALISE_ELEMENT(type);
|
|
SERIALISE_ELEMENT(indices);
|
|
SERIALISE_ELEMENT(drawcount);
|
|
SERIALISE_ELEMENT_ARRAY(basevertex, drawcount);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
std::vector<const void *> inds;
|
|
inds.reserve(drawcount);
|
|
for(GLsizei i = 0; i < drawcount; i++)
|
|
inds.push_back((const void *)indices[i]);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
GL.glMultiDrawElementsBaseVertex(mode, count, type, inds.data(), drawcount, basevertex);
|
|
|
|
uint32_t IdxSize = GetIdxSize(type);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%i)", ToStr(gl_CurChunk).c_str(), drawcount);
|
|
|
|
draw.flags |= DrawFlags::MultiDraw;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
draw.indexByteWidth = IdxSize;
|
|
|
|
AddDrawcall(draw, false);
|
|
|
|
m_DrawcallStack.push_back(&m_DrawcallStack.back()->children.back());
|
|
|
|
for(GLsizei i = 0; i < drawcount; i++)
|
|
{
|
|
m_CurEventID++;
|
|
|
|
DrawcallDescription multidraw;
|
|
multidraw.drawIndex = i;
|
|
multidraw.numIndices = count[i];
|
|
multidraw.indexOffset = (uint32_t)(indices[i] & 0xFFFFFFFF);
|
|
multidraw.baseVertex = basevertex[i];
|
|
|
|
multidraw.indexOffset /= IdxSize;
|
|
|
|
multidraw.name =
|
|
StringFormat::Fmt("%s[%i](%u)", ToStr(gl_CurChunk).c_str(), i, multidraw.numIndices);
|
|
|
|
multidraw.flags |= DrawFlags::Drawcall | DrawFlags::Indexed;
|
|
|
|
multidraw.topology = MakePrimitiveTopology(mode);
|
|
multidraw.indexByteWidth = IdxSize;
|
|
|
|
AddEvent();
|
|
AddDrawcall(multidraw, true);
|
|
}
|
|
|
|
m_DrawcallStack.pop_back();
|
|
}
|
|
else if(IsActiveReplaying(m_State))
|
|
{
|
|
size_t i = 0;
|
|
for(; i < m_Events.size(); i++)
|
|
{
|
|
if(m_Events[i].eventId >= m_CurEventID)
|
|
break;
|
|
}
|
|
|
|
while(i > 1 && m_Events[i - 1].fileOffset == m_Events[i].fileOffset)
|
|
i--;
|
|
|
|
uint32_t baseEventID = m_Events[i].eventId;
|
|
|
|
if(m_LastEventID < baseEventID)
|
|
{
|
|
// To add the multidraw, we made an event N that is the 'parent' marker, then
|
|
// N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected
|
|
// then we'll replay up to N but not N+1, so just do nothing - we DON'T want to draw
|
|
// the first sub-draw in that range.
|
|
}
|
|
else if(m_FirstEventID <= baseEventID && m_LastEventID >= baseEventID)
|
|
{
|
|
// if we're replaying part-way into a multidraw, we can replay the first part 'easily'
|
|
// by just reducing the Count parameter to however many we want to replay. This only
|
|
// works if we're replaying from the first multidraw to the nth (n less than Count)
|
|
GL.glMultiDrawElementsBaseVertex(
|
|
mode, count, type, inds.data(),
|
|
RDCMIN((uint32_t)drawcount, m_LastEventID - baseEventID + 1), basevertex);
|
|
}
|
|
else
|
|
{
|
|
// otherwise we do the 'hard' case, draw only one multidraw
|
|
// note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or
|
|
// a single draw.
|
|
//
|
|
// We also need to use the original glMultiDraw command so that gl_DrawID is faithful. In
|
|
// order to preserve the draw index we write a custom multidraw that specifies count == 0
|
|
// for all previous draws.
|
|
RDCASSERT(m_LastEventID == m_FirstEventID);
|
|
|
|
uint32_t drawidx = (m_LastEventID - baseEventID);
|
|
|
|
// zero out the count for all previous draws. This won't be used again so we can safely
|
|
// write over the serialised array.
|
|
GLsizei *modcount = (GLsizei *)count;
|
|
for(uint32_t d = 0; d < drawidx; d++)
|
|
modcount[d] = 0;
|
|
|
|
GL.glMultiDrawElementsBaseVertex(mode, count, type, inds.data(), drawidx + 1, basevertex);
|
|
}
|
|
|
|
m_CurEventID += (uint32_t)drawcount;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glMultiDrawElementsBaseVertex(GLenum mode, const GLsizei *count, GLenum type,
|
|
const void *const *indices, GLsizei drawcount,
|
|
const GLint *basevertex)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(
|
|
GL.glMultiDrawElementsBaseVertex(mode, count, type, indices, drawcount, basevertex));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glMultiDrawElementsBaseVertex(ser, mode, count, type, indices, drawcount, basevertex);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glMultiDrawArraysIndirect(SerialiserType &ser, GLenum mode,
|
|
const void *indirect, GLsizei drawcount,
|
|
GLsizei stride)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT_LOCAL(offset, (uint64_t)indirect);
|
|
SERIALISE_ELEMENT(drawcount);
|
|
SERIALISE_ELEMENT(stride);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(IsLoading(m_State))
|
|
{
|
|
GL.glMultiDrawArraysIndirect(mode, (const void *)offset, drawcount, stride);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%i)", ToStr(gl_CurChunk).c_str(), drawcount);
|
|
|
|
draw.flags |= DrawFlags::MultiDraw;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
|
|
AddDrawcall(draw, false);
|
|
|
|
m_DrawcallStack.push_back(&m_DrawcallStack.back()->children.back());
|
|
|
|
{
|
|
GLuint buf = 0;
|
|
GL.glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING, (GLint *)&buf);
|
|
|
|
m_ResourceUses[GetResourceManager()->GetID(BufferRes(GetCtx(), buf))].push_back(
|
|
EventUsage(m_CurEventID, ResourceUsage::Indirect));
|
|
}
|
|
|
|
GLintptr offs = (GLintptr)offset;
|
|
|
|
SDChunk *baseChunk = m_StructuredFile->chunks.back();
|
|
|
|
for(GLsizei i = 0; i < drawcount; i++)
|
|
{
|
|
m_CurEventID++;
|
|
|
|
DrawArraysIndirectCommand params;
|
|
|
|
GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms);
|
|
|
|
if(stride)
|
|
offs += stride;
|
|
else
|
|
offs += sizeof(params);
|
|
|
|
DrawcallDescription multidraw;
|
|
multidraw.drawIndex = i;
|
|
multidraw.numIndices = params.count;
|
|
multidraw.numInstances = params.instanceCount;
|
|
multidraw.vertexOffset = params.first;
|
|
multidraw.instanceOffset = params.baseInstance;
|
|
|
|
multidraw.name = StringFormat::Fmt("%s[%i](<%u, %u>)", ToStr(gl_CurChunk).c_str(), i,
|
|
multidraw.numIndices, multidraw.numInstances);
|
|
|
|
multidraw.flags |= DrawFlags::Drawcall | DrawFlags::Instanced | DrawFlags::Indirect;
|
|
|
|
multidraw.topology = MakePrimitiveTopology(mode);
|
|
|
|
// add a fake chunk for this individual indirect draw
|
|
SDChunk *fakeChunk = new SDChunk(multidraw.name.c_str());
|
|
fakeChunk->metadata = baseChunk->metadata;
|
|
fakeChunk->metadata.chunkID = (uint32_t)GLChunk::glIndirectSubCommand;
|
|
|
|
{
|
|
StructuredSerialiser structuriser(fakeChunk, ser.GetChunkLookup());
|
|
|
|
structuriser.Serialise<uint32_t>("drawIndex", i);
|
|
structuriser.Serialise<uint64_t>("offset", offs);
|
|
structuriser.Serialise("command", params);
|
|
}
|
|
|
|
m_StructuredFile->chunks.push_back(fakeChunk);
|
|
|
|
AddEvent();
|
|
AddDrawcall(multidraw, true);
|
|
}
|
|
|
|
m_DrawcallStack.pop_back();
|
|
}
|
|
else if(IsActiveReplaying(m_State))
|
|
{
|
|
size_t i = 0;
|
|
for(; i < m_Events.size(); i++)
|
|
{
|
|
if(m_Events[i].eventId >= m_CurEventID)
|
|
break;
|
|
}
|
|
|
|
while(i > 1 && m_Events[i - 1].fileOffset == m_Events[i].fileOffset)
|
|
i--;
|
|
|
|
uint32_t baseEventID = m_Events[i].eventId;
|
|
|
|
if(m_LastEventID < baseEventID)
|
|
{
|
|
// To add the multidraw, we made an event N that is the 'parent' marker, then
|
|
// N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected
|
|
// then we'll replay up to N but not N+1, so just do nothing - we DON'T want to draw
|
|
// the first sub-draw in that range.
|
|
}
|
|
else if(m_FirstEventID <= baseEventID && m_LastEventID >= baseEventID)
|
|
{
|
|
// if we're replaying part-way into a multidraw, we can replay the first part 'easily'
|
|
// by just reducing the Count parameter to however many we want to replay. This only
|
|
// works if we're replaying from the first multidraw to the nth (n less than Count)
|
|
GL.glMultiDrawArraysIndirect(mode, (const void *)offset,
|
|
RDCMIN((uint32_t)drawcount, m_LastEventID - baseEventID + 1),
|
|
stride);
|
|
}
|
|
else
|
|
{
|
|
// otherwise we do the 'hard' case, draw only one multidraw
|
|
// note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or
|
|
// a single draw.
|
|
//
|
|
// We also need to use the original glMultiDraw command so that gl_DrawID is faithful. In
|
|
// order to preserve the draw index we write a custom multidraw that specifies count == 0
|
|
// for all previous draws.
|
|
RDCASSERT(m_LastEventID == m_FirstEventID);
|
|
|
|
uint32_t drawidx = (m_LastEventID - baseEventID);
|
|
|
|
DrawArraysIndirectCommand params;
|
|
|
|
GLintptr offs = (GLintptr)offset;
|
|
if(stride != 0)
|
|
offs += stride * drawidx;
|
|
else
|
|
offs += sizeof(params) * drawidx;
|
|
|
|
GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms);
|
|
|
|
{
|
|
GLint prevBuf = 0;
|
|
GL.glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING, &prevBuf);
|
|
|
|
// get an indirect buffer big enough for all the draws
|
|
GLsizeiptr bufLength = sizeof(params) * (drawidx + 1);
|
|
BindIndirectBuffer(bufLength);
|
|
|
|
DrawArraysIndirectCommand *cmds = (DrawArraysIndirectCommand *)GL.glMapBufferRange(
|
|
eGL_DRAW_INDIRECT_BUFFER, 0, bufLength,
|
|
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
|
|
|
|
// zero out all prior draws
|
|
for(uint32_t d = 0; d < drawidx; d++)
|
|
memset(cmds + d, 0, sizeof(DrawArraysIndirectCommand));
|
|
|
|
// write the actual draw's parameters
|
|
memcpy(cmds + drawidx, ¶ms, sizeof(params));
|
|
|
|
GL.glUnmapBuffer(eGL_DRAW_INDIRECT_BUFFER);
|
|
|
|
// the offset is 0 because it's referring to our custom buffer, stride is 0 because we
|
|
// tightly pack.
|
|
GL.glMultiDrawArraysIndirect(mode, (const void *)0, drawidx + 1, 0);
|
|
|
|
GL.glBindBuffer(eGL_DRAW_INDIRECT_BUFFER, prevBuf);
|
|
}
|
|
}
|
|
|
|
m_CurEventID += drawcount;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glMultiDrawArraysIndirect(GLenum mode, const void *indirect, GLsizei drawcount,
|
|
GLsizei stride)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glMultiDrawArraysIndirect(mode, indirect, drawcount, stride));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glMultiDrawArraysIndirect(ser, mode, indirect, drawcount, stride);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glMultiDrawElementsIndirect(SerialiserType &ser, GLenum mode,
|
|
GLenum type, const void *indirect,
|
|
GLsizei drawcount, GLsizei stride)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT(type);
|
|
SERIALISE_ELEMENT_LOCAL(offset, (uint64_t)indirect);
|
|
SERIALISE_ELEMENT(drawcount);
|
|
SERIALISE_ELEMENT(stride);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
uint32_t IdxSize = GetIdxSize(type);
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(IsLoading(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
|
|
GL.glMultiDrawElementsIndirect(mode, type, (const void *)offset, drawcount, stride);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%i)", ToStr(gl_CurChunk).c_str(), drawcount);
|
|
|
|
draw.flags |= DrawFlags::MultiDraw;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
draw.indexByteWidth = IdxSize;
|
|
|
|
AddDrawcall(draw, false);
|
|
|
|
m_DrawcallStack.push_back(&m_DrawcallStack.back()->children.back());
|
|
|
|
{
|
|
GLuint buf = 0;
|
|
GL.glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING, (GLint *)&buf);
|
|
|
|
m_ResourceUses[GetResourceManager()->GetID(BufferRes(GetCtx(), buf))].push_back(
|
|
EventUsage(m_CurEventID, ResourceUsage::Indirect));
|
|
}
|
|
|
|
GLintptr offs = (GLintptr)offset;
|
|
|
|
SDChunk *baseChunk = m_StructuredFile->chunks.back();
|
|
|
|
for(GLsizei i = 0; i < drawcount; i++)
|
|
{
|
|
m_CurEventID++;
|
|
|
|
DrawElementsIndirectCommand params;
|
|
|
|
GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms);
|
|
|
|
if(stride)
|
|
offs += stride;
|
|
else
|
|
offs += sizeof(params);
|
|
|
|
DrawcallDescription multidraw;
|
|
multidraw.drawIndex = i;
|
|
multidraw.numIndices = params.count;
|
|
multidraw.numInstances = params.instanceCount;
|
|
multidraw.indexOffset = params.firstIndex;
|
|
multidraw.baseVertex = params.baseVertex;
|
|
multidraw.instanceOffset = params.baseInstance;
|
|
|
|
multidraw.name = StringFormat::Fmt("%s[%i](<%u, %u>)", ToStr(gl_CurChunk).c_str(), i,
|
|
multidraw.numIndices, multidraw.numInstances);
|
|
|
|
multidraw.flags |=
|
|
DrawFlags::Drawcall | DrawFlags::Indexed | DrawFlags::Instanced | DrawFlags::Indirect;
|
|
|
|
multidraw.topology = MakePrimitiveTopology(mode);
|
|
multidraw.indexByteWidth = IdxSize;
|
|
|
|
// add a fake chunk for this individual indirect draw
|
|
SDChunk *fakeChunk = new SDChunk(multidraw.name.c_str());
|
|
fakeChunk->metadata = baseChunk->metadata;
|
|
fakeChunk->metadata.chunkID = (uint32_t)GLChunk::glIndirectSubCommand;
|
|
|
|
{
|
|
StructuredSerialiser structuriser(fakeChunk, ser.GetChunkLookup());
|
|
|
|
structuriser.Serialise<uint32_t>("drawIndex", i);
|
|
structuriser.Serialise<uint64_t>("offset", offs);
|
|
structuriser.Serialise("command", params);
|
|
}
|
|
|
|
m_StructuredFile->chunks.push_back(fakeChunk);
|
|
|
|
AddEvent();
|
|
AddDrawcall(multidraw, true);
|
|
}
|
|
|
|
m_DrawcallStack.pop_back();
|
|
}
|
|
else if(IsActiveReplaying(m_State))
|
|
{
|
|
size_t i = 0;
|
|
for(; i < m_Events.size(); i++)
|
|
{
|
|
if(m_Events[i].eventId >= m_CurEventID)
|
|
break;
|
|
}
|
|
|
|
while(i > 1 && m_Events[i - 1].fileOffset == m_Events[i].fileOffset)
|
|
i--;
|
|
|
|
uint32_t baseEventID = m_Events[i].eventId;
|
|
|
|
if(m_LastEventID < baseEventID)
|
|
{
|
|
// To add the multidraw, we made an event N that is the 'parent' marker, then
|
|
// N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected
|
|
// then we'll replay up to N but not N+1, so just do nothing - we DON'T want to draw
|
|
// the first sub-draw in that range.
|
|
}
|
|
else if(m_FirstEventID <= baseEventID && m_LastEventID >= baseEventID)
|
|
{
|
|
// if we're replaying part-way into a multidraw, we can replay the first part 'easily'
|
|
// by just reducing the Count parameter to however many we want to replay. This only
|
|
// works if we're replaying from the first multidraw to the nth (n less than Count)
|
|
GL.glMultiDrawElementsIndirect(mode, type, (const void *)offset,
|
|
RDCMIN((uint32_t)drawcount, m_LastEventID - baseEventID + 1),
|
|
stride);
|
|
}
|
|
else
|
|
{
|
|
// otherwise we do the 'hard' case, draw only one multidraw
|
|
// note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or
|
|
// a single draw.
|
|
//
|
|
// We also need to use the original glMultiDraw command so that gl_DrawID is faithful. In
|
|
// order to preserve the draw index we write a custom multidraw that specifies count == 0
|
|
// for all previous draws.
|
|
RDCASSERT(m_LastEventID == m_FirstEventID);
|
|
|
|
uint32_t drawidx = (m_LastEventID - baseEventID);
|
|
|
|
DrawElementsIndirectCommand params;
|
|
|
|
GLintptr offs = (GLintptr)offset;
|
|
if(stride != 0)
|
|
offs += stride * drawidx;
|
|
else
|
|
offs += sizeof(params) * drawidx;
|
|
|
|
GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms);
|
|
|
|
{
|
|
GLint prevBuf = 0;
|
|
GL.glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING, &prevBuf);
|
|
|
|
// get an indirect buffer big enough for all the draws
|
|
GLsizeiptr bufLength = sizeof(params) * (drawidx + 1);
|
|
BindIndirectBuffer(bufLength);
|
|
|
|
DrawElementsIndirectCommand *cmds = (DrawElementsIndirectCommand *)GL.glMapBufferRange(
|
|
eGL_DRAW_INDIRECT_BUFFER, 0, bufLength,
|
|
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
|
|
|
|
// zero out all prior draws
|
|
for(uint32_t d = 0; d < drawidx; d++)
|
|
memset(cmds + d, 0, sizeof(DrawElementsIndirectCommand));
|
|
|
|
// write the actual draw's parameters
|
|
memcpy(cmds + drawidx, ¶ms, sizeof(params));
|
|
|
|
GL.glUnmapBuffer(eGL_DRAW_INDIRECT_BUFFER);
|
|
|
|
// the offset is 0 because it's referring to our custom buffer, stride is 0 because we
|
|
// tightly pack.
|
|
GL.glMultiDrawElementsIndirect(mode, type, (const void *)0, drawidx + 1, 0);
|
|
|
|
GL.glBindBuffer(eGL_DRAW_INDIRECT_BUFFER, prevBuf);
|
|
}
|
|
}
|
|
|
|
m_CurEventID += drawcount;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glMultiDrawElementsIndirect(GLenum mode, GLenum type, const void *indirect,
|
|
GLsizei drawcount, GLsizei stride)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glMultiDrawElementsIndirect(mode, type, indirect, drawcount, stride));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glMultiDrawElementsIndirect(ser, mode, type, indirect, drawcount, stride);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glMultiDrawArraysIndirectCount(SerialiserType &ser, GLenum mode,
|
|
const void *indirect,
|
|
GLintptr drawcountPtr,
|
|
GLsizei maxdrawcount, GLsizei stride)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT_LOCAL(offset, (uint64_t)indirect);
|
|
SERIALISE_ELEMENT_LOCAL(drawcount, (uint64_t)drawcountPtr);
|
|
SERIALISE_ELEMENT(maxdrawcount);
|
|
SERIALISE_ELEMENT(stride);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GLsizei realdrawcount = 0;
|
|
|
|
GL.glGetBufferSubData(eGL_PARAMETER_BUFFER, (GLintptr)drawcount, sizeof(realdrawcount),
|
|
&realdrawcount);
|
|
|
|
realdrawcount = RDCMIN(maxdrawcount, realdrawcount);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
GL.glMultiDrawArraysIndirectCount(mode, (const void *)offset, (GLintptr)drawcount,
|
|
maxdrawcount, stride);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(<%i>)", ToStr(gl_CurChunk).c_str(), realdrawcount);
|
|
|
|
draw.flags |= DrawFlags::MultiDraw;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
|
|
AddDrawcall(draw, false);
|
|
|
|
m_DrawcallStack.push_back(&m_DrawcallStack.back()->children.back());
|
|
|
|
{
|
|
GLuint buf = 0;
|
|
GL.glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING, (GLint *)&buf);
|
|
|
|
m_ResourceUses[GetResourceManager()->GetID(BufferRes(GetCtx(), buf))].push_back(
|
|
EventUsage(m_CurEventID, ResourceUsage::Indirect));
|
|
}
|
|
|
|
GLintptr offs = (GLintptr)offset;
|
|
|
|
SDChunk *baseChunk = m_StructuredFile->chunks.back();
|
|
|
|
for(GLsizei i = 0; i < realdrawcount; i++)
|
|
{
|
|
m_CurEventID++;
|
|
|
|
DrawArraysIndirectCommand params;
|
|
|
|
GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms);
|
|
|
|
if(stride)
|
|
offs += stride;
|
|
else
|
|
offs += sizeof(params);
|
|
|
|
DrawcallDescription multidraw;
|
|
multidraw.drawIndex = i;
|
|
multidraw.numIndices = params.count;
|
|
multidraw.numInstances = params.instanceCount;
|
|
multidraw.vertexOffset = params.first;
|
|
multidraw.instanceOffset = params.baseInstance;
|
|
|
|
multidraw.name = StringFormat::Fmt("%s[%i](<%u, %u>)", ToStr(gl_CurChunk).c_str(), i,
|
|
multidraw.numIndices, multidraw.numInstances);
|
|
|
|
multidraw.flags |= DrawFlags::Drawcall | DrawFlags::Instanced | DrawFlags::Indirect;
|
|
|
|
multidraw.topology = MakePrimitiveTopology(mode);
|
|
|
|
// add a fake chunk for this individual indirect draw
|
|
SDChunk *fakeChunk = new SDChunk(multidraw.name.c_str());
|
|
fakeChunk->metadata = baseChunk->metadata;
|
|
fakeChunk->metadata.chunkID = (uint32_t)GLChunk::glIndirectSubCommand;
|
|
|
|
{
|
|
StructuredSerialiser structuriser(fakeChunk, ser.GetChunkLookup());
|
|
|
|
structuriser.Serialise<uint32_t>("drawIndex", i);
|
|
structuriser.Serialise<uint64_t>("offset", offs);
|
|
structuriser.Serialise("command", params);
|
|
}
|
|
|
|
m_StructuredFile->chunks.push_back(fakeChunk);
|
|
|
|
AddEvent();
|
|
AddDrawcall(multidraw, true);
|
|
}
|
|
|
|
m_DrawcallStack.pop_back();
|
|
}
|
|
else if(IsActiveReplaying(m_State))
|
|
{
|
|
size_t i = 0;
|
|
for(; i < m_Events.size(); i++)
|
|
{
|
|
if(m_Events[i].eventId >= m_CurEventID)
|
|
break;
|
|
}
|
|
|
|
while(i > 1 && m_Events[i - 1].fileOffset == m_Events[i].fileOffset)
|
|
i--;
|
|
|
|
uint32_t baseEventID = m_Events[i].eventId;
|
|
|
|
if(m_LastEventID < baseEventID)
|
|
{
|
|
// To add the multidraw, we made an event N that is the 'parent' marker, then
|
|
// N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected
|
|
// then we'll replay up to N but not N+1, so just do nothing - we DON'T want to draw
|
|
// the first sub-draw in that range.
|
|
}
|
|
else if(m_FirstEventID <= baseEventID && m_LastEventID >= baseEventID)
|
|
{
|
|
// if we're replaying part-way into a multidraw, we can replay the first part 'easily'
|
|
// by just reducing the Count parameter to however many we want to replay. This only
|
|
// works if we're replaying from the first multidraw to the nth (n less than Count)
|
|
GL.glMultiDrawArraysIndirect(mode, (const void *)offset,
|
|
RDCMIN((uint32_t)drawcount, m_LastEventID - baseEventID + 1),
|
|
stride);
|
|
}
|
|
else
|
|
{
|
|
// otherwise we do the 'hard' case, draw only one multidraw
|
|
// note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or
|
|
// a single draw.
|
|
//
|
|
// We also need to use the original glMultiDraw command so that gl_DrawID is faithful. In
|
|
// order to preserve the draw index we write a custom multidraw that specifies count == 0
|
|
// for all previous draws.
|
|
RDCASSERT(m_LastEventID == m_FirstEventID);
|
|
|
|
uint32_t drawidx = (m_LastEventID - baseEventID);
|
|
|
|
DrawArraysIndirectCommand params;
|
|
|
|
GLintptr offs = (GLintptr)offset;
|
|
if(stride != 0)
|
|
offs += stride * drawidx;
|
|
else
|
|
offs += sizeof(params) * drawidx;
|
|
|
|
GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms);
|
|
|
|
GL.glDrawArraysInstancedBaseInstance(mode, params.first, params.count, params.instanceCount,
|
|
params.baseInstance);
|
|
|
|
{
|
|
GLint prevBuf = 0;
|
|
GL.glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING, &prevBuf);
|
|
|
|
// get an indirect buffer big enough for all the draws
|
|
GLsizeiptr bufLength = sizeof(params) * (drawidx + 1);
|
|
BindIndirectBuffer(bufLength);
|
|
|
|
DrawArraysIndirectCommand *cmds = (DrawArraysIndirectCommand *)GL.glMapBufferRange(
|
|
eGL_DRAW_INDIRECT_BUFFER, 0, bufLength,
|
|
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
|
|
|
|
// zero out all prior draws
|
|
for(uint32_t d = 0; d < drawidx; d++)
|
|
memset(cmds + d, 0, sizeof(DrawArraysIndirectCommand));
|
|
|
|
// write the actual draw's parameters
|
|
memcpy(cmds + drawidx, ¶ms, sizeof(params));
|
|
|
|
GL.glUnmapBuffer(eGL_DRAW_INDIRECT_BUFFER);
|
|
|
|
// the offset is 0 because it's referring to our custom buffer, stride is 0 because we
|
|
// tightly pack.
|
|
GL.glMultiDrawArraysIndirect(mode, (const void *)0, drawidx + 1, 0);
|
|
|
|
GL.glBindBuffer(eGL_DRAW_INDIRECT_BUFFER, prevBuf);
|
|
}
|
|
}
|
|
|
|
m_CurEventID += realdrawcount;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glMultiDrawArraysIndirectCount(GLenum mode, const void *indirect,
|
|
GLintptr drawcount, GLsizei maxdrawcount,
|
|
GLsizei stride)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(
|
|
GL.glMultiDrawArraysIndirectCount(mode, indirect, drawcount, maxdrawcount, stride));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glMultiDrawArraysIndirectCount(ser, mode, indirect, drawcount, maxdrawcount, stride);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glMultiDrawElementsIndirectCount(SerialiserType &ser, GLenum mode,
|
|
GLenum type, const void *indirect,
|
|
GLintptr drawcountPtr,
|
|
GLsizei maxdrawcount, GLsizei stride)
|
|
{
|
|
SERIALISE_ELEMENT(mode);
|
|
SERIALISE_ELEMENT(type);
|
|
SERIALISE_ELEMENT_LOCAL(offset, (uint64_t)indirect);
|
|
SERIALISE_ELEMENT_LOCAL(drawcount, (uint64_t)drawcountPtr);
|
|
SERIALISE_ELEMENT(maxdrawcount);
|
|
SERIALISE_ELEMENT(stride);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
uint32_t IdxSize = GetIdxSize(type);
|
|
|
|
GLsizei realdrawcount = 0;
|
|
|
|
GL.glGetBufferSubData(eGL_PARAMETER_BUFFER, (GLintptr)drawcount, sizeof(realdrawcount),
|
|
&realdrawcount);
|
|
|
|
realdrawcount = RDCMIN(maxdrawcount, realdrawcount);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
GL.glMultiDrawElementsIndirectCount(mode, type, (const void *)offset, (GLintptr)drawcount,
|
|
maxdrawcount, stride);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(<%i>)", ToStr(gl_CurChunk).c_str(), realdrawcount);
|
|
|
|
draw.flags |= DrawFlags::MultiDraw;
|
|
|
|
draw.topology = MakePrimitiveTopology(mode);
|
|
draw.indexByteWidth = IdxSize;
|
|
|
|
AddDrawcall(draw, false);
|
|
|
|
m_DrawcallStack.push_back(&m_DrawcallStack.back()->children.back());
|
|
|
|
{
|
|
GLuint buf = 0;
|
|
GL.glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING, (GLint *)&buf);
|
|
|
|
m_ResourceUses[GetResourceManager()->GetID(BufferRes(GetCtx(), buf))].push_back(
|
|
EventUsage(m_CurEventID, ResourceUsage::Indirect));
|
|
}
|
|
|
|
GLintptr offs = (GLintptr)offset;
|
|
|
|
SDChunk *baseChunk = m_StructuredFile->chunks.back();
|
|
|
|
for(GLsizei i = 0; i < realdrawcount; i++)
|
|
{
|
|
m_CurEventID++;
|
|
|
|
DrawElementsIndirectCommand params;
|
|
|
|
GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms);
|
|
|
|
if(stride)
|
|
offs += stride;
|
|
else
|
|
offs += sizeof(params);
|
|
|
|
DrawcallDescription multidraw;
|
|
multidraw.drawIndex = i;
|
|
multidraw.numIndices = params.count;
|
|
multidraw.numInstances = params.instanceCount;
|
|
multidraw.indexOffset = params.firstIndex;
|
|
multidraw.baseVertex = params.baseVertex;
|
|
multidraw.instanceOffset = params.baseInstance;
|
|
|
|
multidraw.name = StringFormat::Fmt("%s[%i](<%u, %u>)", ToStr(gl_CurChunk).c_str(), i,
|
|
multidraw.numIndices, multidraw.numInstances);
|
|
|
|
multidraw.flags |=
|
|
DrawFlags::Drawcall | DrawFlags::Indexed | DrawFlags::Instanced | DrawFlags::Indirect;
|
|
|
|
multidraw.topology = MakePrimitiveTopology(mode);
|
|
multidraw.indexByteWidth = IdxSize;
|
|
|
|
// add a fake chunk for this individual indirect draw
|
|
SDChunk *fakeChunk = new SDChunk(multidraw.name.c_str());
|
|
fakeChunk->metadata = baseChunk->metadata;
|
|
fakeChunk->metadata.chunkID = (uint32_t)GLChunk::glIndirectSubCommand;
|
|
|
|
{
|
|
StructuredSerialiser structuriser(fakeChunk, ser.GetChunkLookup());
|
|
|
|
structuriser.Serialise<uint32_t>("drawIndex", i);
|
|
structuriser.Serialise<uint64_t>("offset", offs);
|
|
structuriser.Serialise("command", params);
|
|
}
|
|
|
|
m_StructuredFile->chunks.push_back(fakeChunk);
|
|
|
|
AddEvent();
|
|
AddDrawcall(multidraw, true);
|
|
}
|
|
|
|
m_DrawcallStack.pop_back();
|
|
}
|
|
else if(IsActiveReplaying(m_State))
|
|
{
|
|
size_t i = 0;
|
|
for(; i < m_Events.size(); i++)
|
|
{
|
|
if(m_Events[i].eventId >= m_CurEventID)
|
|
break;
|
|
}
|
|
|
|
while(i > 1 && m_Events[i - 1].fileOffset == m_Events[i].fileOffset)
|
|
i--;
|
|
|
|
uint32_t baseEventID = m_Events[i].eventId;
|
|
|
|
if(m_LastEventID < baseEventID)
|
|
{
|
|
// To add the multidraw, we made an event N that is the 'parent' marker, then
|
|
// N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected
|
|
// then we'll replay up to N but not N+1, so just do nothing - we DON'T want to draw
|
|
// the first sub-draw in that range.
|
|
}
|
|
else if(m_FirstEventID <= baseEventID && m_LastEventID >= baseEventID)
|
|
{
|
|
// if we're replaying part-way into a multidraw, we can replay the first part 'easily'
|
|
// by just reducing the Count parameter to however many we want to replay. This only
|
|
// works if we're replaying from the first multidraw to the nth (n less than Count)
|
|
GL.glMultiDrawElementsIndirect(mode, type, (const void *)offset,
|
|
RDCMIN((uint32_t)drawcount, m_LastEventID - baseEventID + 1),
|
|
stride);
|
|
}
|
|
else
|
|
{
|
|
// otherwise we do the 'hard' case, draw only one multidraw
|
|
// note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or
|
|
// a single draw.
|
|
//
|
|
// We also need to use the original glMultiDraw command so that gl_DrawID is faithful. In
|
|
// order to preserve the draw index we write a custom multidraw that specifies count == 0
|
|
// for all previous draws.
|
|
RDCASSERT(m_LastEventID == m_FirstEventID);
|
|
|
|
uint32_t drawidx = (m_LastEventID - baseEventID);
|
|
|
|
DrawElementsIndirectCommand params;
|
|
|
|
GLintptr offs = (GLintptr)offset;
|
|
if(stride != 0)
|
|
offs += stride * drawidx;
|
|
else
|
|
offs += sizeof(params) * drawidx;
|
|
|
|
GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms);
|
|
|
|
{
|
|
GLint prevBuf = 0;
|
|
GL.glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING, &prevBuf);
|
|
|
|
// get an indirect buffer big enough for all the draws
|
|
GLsizeiptr bufLength = sizeof(params) * (drawidx + 1);
|
|
BindIndirectBuffer(bufLength);
|
|
|
|
DrawElementsIndirectCommand *cmds = (DrawElementsIndirectCommand *)GL.glMapBufferRange(
|
|
eGL_DRAW_INDIRECT_BUFFER, 0, bufLength,
|
|
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
|
|
|
|
// zero out all prior draws
|
|
for(uint32_t d = 0; d < drawidx; d++)
|
|
memset(cmds + d, 0, sizeof(DrawElementsIndirectCommand));
|
|
|
|
// write the actual draw's parameters
|
|
memcpy(cmds + drawidx, ¶ms, sizeof(params));
|
|
|
|
GL.glUnmapBuffer(eGL_DRAW_INDIRECT_BUFFER);
|
|
|
|
// the offset is 0 because it's referring to our custom buffer, stride is 0 because we
|
|
// tightly pack.
|
|
GL.glMultiDrawElementsIndirect(mode, type, (const void *)0, drawidx + 1, 0);
|
|
|
|
GL.glBindBuffer(eGL_DRAW_INDIRECT_BUFFER, prevBuf);
|
|
}
|
|
}
|
|
|
|
m_CurEventID += realdrawcount;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glMultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void *indirect,
|
|
GLintptr drawcount, GLsizei maxdrawcount,
|
|
GLsizei stride)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(
|
|
GL.glMultiDrawElementsIndirectCount(mode, type, indirect, drawcount, maxdrawcount, stride));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glMultiDrawElementsIndirectCount(ser, mode, type, indirect, drawcount, maxdrawcount,
|
|
stride);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glClearNamedFramebufferfv(SerialiserType &ser,
|
|
GLuint framebufferHandle, GLenum buffer,
|
|
GLint drawbuffer, const GLfloat *value)
|
|
{
|
|
SERIALISE_ELEMENT_LOCAL(framebuffer, FramebufferRes(GetCtx(), framebufferHandle));
|
|
SERIALISE_ELEMENT(buffer);
|
|
SERIALISE_ELEMENT(drawbuffer);
|
|
SERIALISE_ELEMENT_ARRAY(value, buffer == eGL_DEPTH ? 1 : 4);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(framebuffer.name == 0)
|
|
framebuffer.name = m_CurrentDefaultFBO;
|
|
|
|
// use ARB_direct_state_access functions here as we use EXT_direct_state_access elsewhere. If
|
|
// we are running without ARB_dsa support, these functions are emulated in the obvious way. This
|
|
// is necessary since these functions can be serialised even if ARB_dsa was not used originally,
|
|
// and we need to support this case.
|
|
GL.glClearNamedFramebufferfv(framebuffer.name, buffer, drawbuffer, value);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
std::string name;
|
|
|
|
if(buffer == eGL_DEPTH)
|
|
name = StringFormat::Fmt("%s(%s, %i, %f)", ToStr(gl_CurChunk).c_str(),
|
|
ToStr(buffer).c_str(), drawbuffer, value[0]);
|
|
else
|
|
name = StringFormat::Fmt("%s(%s, %i, %f, %f, %f, %f)", ToStr(gl_CurChunk).c_str(),
|
|
ToStr(buffer).c_str(), drawbuffer, value[0], value[1], value[2],
|
|
value[2]);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = name;
|
|
draw.flags |= DrawFlags::Clear;
|
|
if(buffer == eGL_COLOR)
|
|
draw.flags |= DrawFlags::ClearColor;
|
|
else
|
|
draw.flags |= DrawFlags::ClearDepthStencil;
|
|
|
|
GLuint attachment = 0;
|
|
GLenum attachName =
|
|
buffer == eGL_COLOR ? GLenum(eGL_COLOR_ATTACHMENT0 + drawbuffer) : eGL_DEPTH_ATTACHMENT;
|
|
GLenum type = eGL_TEXTURE;
|
|
GL.glGetNamedFramebufferAttachmentParameterivEXT(
|
|
framebuffer.name, attachName, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint *)&attachment);
|
|
GL.glGetNamedFramebufferAttachmentParameterivEXT(
|
|
framebuffer.name, attachName, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint *)&type);
|
|
|
|
if(attachment)
|
|
{
|
|
ResourceId id;
|
|
|
|
if(type == eGL_TEXTURE)
|
|
id = GetResourceManager()->GetID(TextureRes(GetCtx(), attachment));
|
|
else
|
|
id = GetResourceManager()->GetID(RenderbufferRes(GetCtx(), attachment));
|
|
|
|
m_ResourceUses[id].push_back(EventUsage(m_CurEventID, ResourceUsage::Clear));
|
|
draw.copyDestination = GetResourceManager()->GetOriginalID(id);
|
|
}
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glClearNamedFramebufferfv(GLuint framebuffer, GLenum buffer, GLint drawbuffer,
|
|
const GLfloat *value)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glClearNamedFramebufferfv(framebuffer, buffer, drawbuffer, value));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClearNamedFramebufferfv(ser, framebuffer, buffer, drawbuffer, value);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
|
|
GLRenderState state;
|
|
state.FetchState(this);
|
|
state.MarkReferenced(this, false);
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GLRenderState state;
|
|
state.MarkDirty(this);
|
|
}
|
|
}
|
|
|
|
void WrappedOpenGL::glClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat *value)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glClearBufferfv(buffer, drawbuffer, value));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
GLuint framebuffer = 0;
|
|
if(GetCtxData().m_DrawFramebufferRecord)
|
|
framebuffer = GetCtxData().m_DrawFramebufferRecord->Resource.name;
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClearNamedFramebufferfv(ser, framebuffer, buffer, drawbuffer, value);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glClearNamedFramebufferiv(SerialiserType &ser,
|
|
GLuint framebufferHandle, GLenum buffer,
|
|
GLint drawbuffer, const GLint *value)
|
|
{
|
|
SERIALISE_ELEMENT_LOCAL(framebuffer, FramebufferRes(GetCtx(), framebufferHandle));
|
|
SERIALISE_ELEMENT(buffer);
|
|
SERIALISE_ELEMENT(drawbuffer);
|
|
SERIALISE_ELEMENT_ARRAY(value, buffer == eGL_STENCIL ? 1 : 4);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(framebuffer.name == 0)
|
|
framebuffer.name = m_CurrentDefaultFBO;
|
|
|
|
// use ARB_direct_state_access functions here as we use EXT_direct_state_access elsewhere. If
|
|
// we are running without ARB_dsa support, these functions are emulated in the obvious way. This
|
|
// is necessary since these functions can be serialised even if ARB_dsa was not used originally,
|
|
// and we need to support this case.
|
|
GL.glClearNamedFramebufferiv(framebuffer.name, buffer, drawbuffer, value);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
std::string name;
|
|
|
|
if(buffer == eGL_STENCIL)
|
|
name = StringFormat::Fmt("%s(%s, %i, %i)", ToStr(gl_CurChunk).c_str(),
|
|
ToStr(buffer).c_str(), drawbuffer, value[0]);
|
|
else
|
|
name = StringFormat::Fmt("%s(%s, %i, %i, %i, %i, %i)", ToStr(gl_CurChunk).c_str(),
|
|
ToStr(buffer).c_str(), drawbuffer, value[0], value[1], value[2],
|
|
value[3]);
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = name;
|
|
draw.flags |= DrawFlags::Clear;
|
|
if(buffer == eGL_COLOR)
|
|
draw.flags |= DrawFlags::ClearColor;
|
|
else
|
|
draw.flags |= DrawFlags::ClearDepthStencil;
|
|
|
|
GLuint attachment = 0;
|
|
GLenum attachName =
|
|
buffer == eGL_COLOR ? GLenum(eGL_COLOR_ATTACHMENT0 + drawbuffer) : eGL_STENCIL_ATTACHMENT;
|
|
GLenum type = eGL_TEXTURE;
|
|
GL.glGetNamedFramebufferAttachmentParameterivEXT(
|
|
framebuffer.name, attachName, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint *)&attachment);
|
|
GL.glGetNamedFramebufferAttachmentParameterivEXT(
|
|
framebuffer.name, attachName, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint *)&type);
|
|
|
|
if(attachment)
|
|
{
|
|
ResourceId id;
|
|
|
|
if(type == eGL_TEXTURE)
|
|
id = GetResourceManager()->GetID(TextureRes(GetCtx(), attachment));
|
|
else
|
|
id = GetResourceManager()->GetID(RenderbufferRes(GetCtx(), attachment));
|
|
|
|
m_ResourceUses[id].push_back(EventUsage(m_CurEventID, ResourceUsage::Clear));
|
|
draw.copyDestination = GetResourceManager()->GetOriginalID(id);
|
|
}
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glClearNamedFramebufferiv(GLuint framebuffer, GLenum buffer, GLint drawbuffer,
|
|
const GLint *value)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glClearNamedFramebufferiv(framebuffer, buffer, drawbuffer, value));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClearNamedFramebufferiv(ser, framebuffer, buffer, drawbuffer, value);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
}
|
|
|
|
void WrappedOpenGL::glClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint *value)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glClearBufferiv(buffer, drawbuffer, value));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
GLuint framebuffer = 0;
|
|
if(GetCtxData().m_DrawFramebufferRecord)
|
|
framebuffer = GetCtxData().m_DrawFramebufferRecord->Resource.name;
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClearNamedFramebufferiv(ser, framebuffer, buffer, drawbuffer, value);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glClearNamedFramebufferuiv(SerialiserType &ser,
|
|
GLuint framebufferHandle, GLenum buffer,
|
|
GLint drawbuffer, const GLuint *value)
|
|
{
|
|
SERIALISE_ELEMENT_LOCAL(framebuffer, FramebufferRes(GetCtx(), framebufferHandle));
|
|
SERIALISE_ELEMENT(buffer);
|
|
SERIALISE_ELEMENT(drawbuffer);
|
|
SERIALISE_ELEMENT_ARRAY(value, 4);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(framebuffer.name == 0)
|
|
framebuffer.name = m_CurrentDefaultFBO;
|
|
|
|
// use ARB_direct_state_access functions here as we use EXT_direct_state_access elsewhere. If
|
|
// we are running without ARB_dsa support, these functions are emulated in the obvious way. This
|
|
// is necessary since these functions can be serialised even if ARB_dsa was not used originally,
|
|
// and we need to support this case.
|
|
GL.glClearNamedFramebufferuiv(framebuffer.name, buffer, drawbuffer, value);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%s, %i, %u, %u, %u, %u)", ToStr(gl_CurChunk).c_str(),
|
|
ToStr(buffer).c_str(), drawbuffer, value[0], value[1], value[2],
|
|
value[3]);
|
|
|
|
draw.flags |= DrawFlags::Clear | DrawFlags::ClearColor;
|
|
|
|
GLuint attachment = 0;
|
|
GLenum attachName = GLenum(eGL_COLOR_ATTACHMENT0 + drawbuffer);
|
|
GLenum type = eGL_TEXTURE;
|
|
GL.glGetNamedFramebufferAttachmentParameterivEXT(
|
|
framebuffer.name, attachName, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint *)&attachment);
|
|
GL.glGetNamedFramebufferAttachmentParameterivEXT(
|
|
framebuffer.name, attachName, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint *)&type);
|
|
|
|
if(attachment)
|
|
{
|
|
ResourceId id;
|
|
|
|
if(type == eGL_TEXTURE)
|
|
id = GetResourceManager()->GetID(TextureRes(GetCtx(), attachment));
|
|
else
|
|
id = GetResourceManager()->GetID(RenderbufferRes(GetCtx(), attachment));
|
|
|
|
m_ResourceUses[id].push_back(EventUsage(m_CurEventID, ResourceUsage::Clear));
|
|
draw.copyDestination = GetResourceManager()->GetOriginalID(id);
|
|
}
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glClearNamedFramebufferuiv(GLuint framebuffer, GLenum buffer, GLint drawbuffer,
|
|
const GLuint *value)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glClearNamedFramebufferuiv(framebuffer, buffer, drawbuffer, value));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClearNamedFramebufferuiv(ser, framebuffer, buffer, drawbuffer, value);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
}
|
|
|
|
void WrappedOpenGL::glClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint *value)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glClearBufferuiv(buffer, drawbuffer, value));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
GLuint framebuffer = 0;
|
|
if(GetCtxData().m_DrawFramebufferRecord)
|
|
framebuffer = GetCtxData().m_DrawFramebufferRecord->Resource.name;
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClearNamedFramebufferuiv(ser, framebuffer, buffer, drawbuffer, value);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glClearNamedFramebufferfi(SerialiserType &ser, GLuint framebufferHandle,
|
|
GLenum buffer, GLint drawbuffer,
|
|
GLfloat depth, GLint stencil)
|
|
{
|
|
SERIALISE_ELEMENT_LOCAL(framebuffer, FramebufferRes(GetCtx(), framebufferHandle));
|
|
SERIALISE_ELEMENT(buffer);
|
|
SERIALISE_ELEMENT(drawbuffer);
|
|
SERIALISE_ELEMENT(depth);
|
|
SERIALISE_ELEMENT(stencil);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
if(framebuffer.name == 0)
|
|
framebuffer.name = m_CurrentDefaultFBO;
|
|
|
|
// use ARB_direct_state_access functions here as we use EXT_direct_state_access elsewhere. If
|
|
// we are running without ARB_dsa support, these functions are emulated in the obvious way. This
|
|
// is necessary since these functions can be serialised even if ARB_dsa was not used originally,
|
|
// and we need to support this case.
|
|
GL.glClearNamedFramebufferfi(framebuffer.name, buffer, drawbuffer, depth, stencil);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = StringFormat::Fmt("%s(%f, %i)", ToStr(gl_CurChunk).c_str(), depth, stencil);
|
|
draw.flags |= DrawFlags::Clear | DrawFlags::ClearDepthStencil;
|
|
|
|
GLuint attachment = 0;
|
|
GLenum type = eGL_TEXTURE;
|
|
GL.glGetNamedFramebufferAttachmentParameterivEXT(framebuffer.name, eGL_DEPTH_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
|
|
(GLint *)&attachment);
|
|
GL.glGetNamedFramebufferAttachmentParameterivEXT(framebuffer.name, eGL_DEPTH_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
|
|
(GLint *)&type);
|
|
|
|
if(attachment)
|
|
{
|
|
ResourceId id;
|
|
|
|
if(type == eGL_TEXTURE)
|
|
id = GetResourceManager()->GetID(TextureRes(GetCtx(), attachment));
|
|
else
|
|
id = GetResourceManager()->GetID(RenderbufferRes(GetCtx(), attachment));
|
|
|
|
m_ResourceUses[id].push_back(EventUsage(m_CurEventID, ResourceUsage::Clear));
|
|
draw.copyDestination = GetResourceManager()->GetOriginalID(id);
|
|
}
|
|
|
|
AddDrawcall(draw, true);
|
|
|
|
attachment = 0;
|
|
type = eGL_TEXTURE;
|
|
GL.glGetNamedFramebufferAttachmentParameterivEXT(framebuffer.name, eGL_STENCIL_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
|
|
(GLint *)&attachment);
|
|
GL.glGetNamedFramebufferAttachmentParameterivEXT(framebuffer.name, eGL_STENCIL_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
|
|
(GLint *)&type);
|
|
|
|
if(attachment)
|
|
{
|
|
if(type == eGL_TEXTURE)
|
|
m_ResourceUses[GetResourceManager()->GetID(TextureRes(GetCtx(), attachment))].push_back(
|
|
EventUsage(m_CurEventID, ResourceUsage::Clear));
|
|
else
|
|
m_ResourceUses[GetResourceManager()->GetID(RenderbufferRes(GetCtx(), attachment))].push_back(
|
|
EventUsage(m_CurEventID, ResourceUsage::Clear));
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glClearNamedFramebufferfi(GLuint framebuffer, GLenum buffer, GLint drawbuffer,
|
|
GLfloat depth, GLint stencil)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glClearNamedFramebufferfi(framebuffer, buffer, drawbuffer, depth, stencil));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClearNamedFramebufferfi(ser, framebuffer, buffer, drawbuffer, depth, stencil);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
}
|
|
|
|
void WrappedOpenGL::glClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glClearBufferfi(buffer, drawbuffer, depth, stencil));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
GLuint framebuffer = 0;
|
|
if(GetCtxData().m_DrawFramebufferRecord)
|
|
framebuffer = GetCtxData().m_DrawFramebufferRecord->Resource.name;
|
|
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClearNamedFramebufferfi(ser, framebuffer, buffer, drawbuffer, depth, stencil);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glClearNamedBufferDataEXT(SerialiserType &ser, GLuint bufferHandle,
|
|
GLenum internalformat, GLenum format,
|
|
GLenum type, const void *dataPtr)
|
|
{
|
|
SERIALISE_ELEMENT_LOCAL(buffer, BufferRes(GetCtx(), bufferHandle));
|
|
SERIALISE_ELEMENT(internalformat);
|
|
SERIALISE_ELEMENT(format);
|
|
SERIALISE_ELEMENT(type);
|
|
|
|
uint64_t data[4] = {0};
|
|
|
|
if(ser.IsWriting())
|
|
{
|
|
size_t s = 1;
|
|
switch(format)
|
|
{
|
|
default:
|
|
RDCWARN("Unexpected format %x, defaulting to single component", format);
|
|
// fall through
|
|
case eGL_RED:
|
|
case eGL_RED_INTEGER:
|
|
case eGL_GREEN_INTEGER:
|
|
case eGL_BLUE_INTEGER:
|
|
case eGL_DEPTH_COMPONENT:
|
|
case eGL_STENCIL_INDEX: s = 1; break;
|
|
case eGL_RG:
|
|
case eGL_RG_INTEGER:
|
|
case eGL_DEPTH_STENCIL: s = 2; break;
|
|
case eGL_RGB:
|
|
case eGL_BGR:
|
|
case eGL_RGB_INTEGER:
|
|
case eGL_BGR_INTEGER: s = 3; break;
|
|
case eGL_RGBA:
|
|
case eGL_BGRA:
|
|
case eGL_RGBA_INTEGER:
|
|
case eGL_BGRA_INTEGER: s = 4; break;
|
|
}
|
|
switch(type)
|
|
{
|
|
case eGL_UNSIGNED_BYTE:
|
|
case eGL_BYTE: s *= 1; break;
|
|
case eGL_UNSIGNED_SHORT:
|
|
case eGL_SHORT: s *= 2; break;
|
|
case eGL_UNSIGNED_INT:
|
|
case eGL_INT:
|
|
case eGL_FLOAT: s *= 4; break;
|
|
default:
|
|
RDCWARN("Unexpected type %x, defaulting to 1 byte type", type);
|
|
// fall through
|
|
case eGL_UNSIGNED_BYTE_3_3_2:
|
|
case eGL_UNSIGNED_BYTE_2_3_3_REV: s = 1; break;
|
|
case eGL_UNSIGNED_SHORT_5_6_5:
|
|
case eGL_UNSIGNED_SHORT_5_6_5_REV:
|
|
case eGL_UNSIGNED_SHORT_4_4_4_4:
|
|
case eGL_UNSIGNED_SHORT_4_4_4_4_REV:
|
|
case eGL_UNSIGNED_SHORT_5_5_5_1:
|
|
case eGL_UNSIGNED_SHORT_1_5_5_5_REV:
|
|
case eGL_UNSIGNED_INT_8_8_8_8:
|
|
case eGL_UNSIGNED_INT_8_8_8_8_REV: s = 2; break;
|
|
case eGL_UNSIGNED_INT_10_10_10_2:
|
|
case eGL_UNSIGNED_INT_2_10_10_10_REV: s = 4; break;
|
|
}
|
|
if(dataPtr)
|
|
memcpy(data, dataPtr, s);
|
|
else
|
|
memset(data, 0, s);
|
|
}
|
|
|
|
SERIALISE_ELEMENT(data);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glClearNamedBufferDataEXT(buffer.name, internalformat, format, type, (const void *)&data[0]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glClearNamedBufferDataEXT(GLuint buffer, GLenum internalformat, GLenum format,
|
|
GLenum type, const void *data)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glClearNamedBufferDataEXT(buffer, internalformat, format, type, data));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClearNamedBufferDataEXT(ser, buffer, internalformat, format, type, data);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GetResourceManager()->MarkDirtyResource(BufferRes(GetCtx(), buffer));
|
|
}
|
|
}
|
|
|
|
void WrappedOpenGL::glClearBufferData(GLenum target, GLenum internalformat, GLenum format,
|
|
GLenum type, const void *data)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glClearBufferData(target, internalformat, format, type, data));
|
|
|
|
if(IsCaptureMode(m_State))
|
|
{
|
|
GLResourceRecord *record = GetCtxData().m_BufferRecord[BufferIdx(target)];
|
|
RDCASSERTMSG("Couldn't identify implicit object at binding. Mismatched or bad GLuint?", record,
|
|
target);
|
|
|
|
if(record)
|
|
{
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClearNamedBufferDataEXT(ser, record->Resource.name, internalformat, format,
|
|
type, data);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GetResourceManager()->MarkDirtyResource(record->GetResourceID());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glClearNamedBufferSubDataEXT(SerialiserType &ser, GLuint bufferHandle,
|
|
GLenum internalformat, GLintptr offsetPtr,
|
|
GLsizeiptr sizePtr, GLenum format,
|
|
GLenum type, const void *dataPtr)
|
|
{
|
|
SERIALISE_ELEMENT_LOCAL(buffer, BufferRes(GetCtx(), bufferHandle));
|
|
SERIALISE_ELEMENT(internalformat);
|
|
SERIALISE_ELEMENT_LOCAL(offset, (uint64_t)offsetPtr);
|
|
SERIALISE_ELEMENT_LOCAL(size, (uint64_t)sizePtr);
|
|
SERIALISE_ELEMENT(format);
|
|
SERIALISE_ELEMENT(type);
|
|
|
|
uint64_t data[4] = {0};
|
|
|
|
if(ser.IsWriting())
|
|
{
|
|
size_t s = 1;
|
|
switch(format)
|
|
{
|
|
default:
|
|
RDCWARN("Unexpected format %x, defaulting to single component", format);
|
|
// fall through
|
|
case eGL_RED:
|
|
case eGL_RED_INTEGER:
|
|
case eGL_GREEN_INTEGER:
|
|
case eGL_BLUE_INTEGER:
|
|
case eGL_DEPTH_COMPONENT:
|
|
case eGL_STENCIL_INDEX: s = 1; break;
|
|
case eGL_RG:
|
|
case eGL_RG_INTEGER:
|
|
case eGL_DEPTH_STENCIL: s = 2; break;
|
|
case eGL_RGB:
|
|
case eGL_BGR:
|
|
case eGL_RGB_INTEGER:
|
|
case eGL_BGR_INTEGER: s = 3; break;
|
|
case eGL_RGBA:
|
|
case eGL_BGRA:
|
|
case eGL_RGBA_INTEGER:
|
|
case eGL_BGRA_INTEGER: s = 4; break;
|
|
}
|
|
switch(type)
|
|
{
|
|
case eGL_UNSIGNED_BYTE:
|
|
case eGL_BYTE: s *= 1; break;
|
|
case eGL_UNSIGNED_SHORT:
|
|
case eGL_SHORT: s *= 2; break;
|
|
case eGL_UNSIGNED_INT:
|
|
case eGL_INT:
|
|
case eGL_FLOAT: s *= 4; break;
|
|
default:
|
|
RDCWARN("Unexpected type %x, defaulting to 1 byte type", type);
|
|
// fall through
|
|
case eGL_UNSIGNED_BYTE_3_3_2:
|
|
case eGL_UNSIGNED_BYTE_2_3_3_REV: s = 1; break;
|
|
case eGL_UNSIGNED_SHORT_5_6_5:
|
|
case eGL_UNSIGNED_SHORT_5_6_5_REV:
|
|
case eGL_UNSIGNED_SHORT_4_4_4_4:
|
|
case eGL_UNSIGNED_SHORT_4_4_4_4_REV:
|
|
case eGL_UNSIGNED_SHORT_5_5_5_1:
|
|
case eGL_UNSIGNED_SHORT_1_5_5_5_REV:
|
|
case eGL_UNSIGNED_INT_8_8_8_8:
|
|
case eGL_UNSIGNED_INT_8_8_8_8_REV: s = 2; break;
|
|
case eGL_UNSIGNED_INT_10_10_10_2:
|
|
case eGL_UNSIGNED_INT_2_10_10_10_REV: s = 4; break;
|
|
}
|
|
if(dataPtr)
|
|
memcpy(data, dataPtr, s);
|
|
else
|
|
memset(data, 0, s);
|
|
}
|
|
|
|
SERIALISE_ELEMENT(data);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glClearNamedBufferSubDataEXT(buffer.name, internalformat, (GLintptr)offset, (GLsizeiptr)size,
|
|
format, type, (const void *)&data[0]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glClearNamedBufferSubDataEXT(GLuint buffer, GLenum internalformat,
|
|
GLintptr offset, GLsizeiptr size, GLenum format,
|
|
GLenum type, const void *data)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(
|
|
GL.glClearNamedBufferSubDataEXT(buffer, internalformat, offset, size, format, type, data));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClearNamedBufferSubDataEXT(ser, buffer, internalformat, offset, size, format, type,
|
|
data);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GetResourceManager()->MarkDirtyResource(BufferRes(GetCtx(), buffer));
|
|
}
|
|
}
|
|
|
|
void WrappedOpenGL::glClearNamedBufferSubData(GLuint buffer, GLenum internalformat, GLintptr offset,
|
|
GLsizeiptr size, GLenum format, GLenum type,
|
|
const void *data)
|
|
{
|
|
// only difference to EXT function is size parameter, so just upcast
|
|
glClearNamedBufferSubDataEXT(buffer, internalformat, offset, size, format, type, data);
|
|
}
|
|
|
|
void WrappedOpenGL::glClearBufferSubData(GLenum target, GLenum internalformat, GLintptr offset,
|
|
GLsizeiptr size, GLenum format, GLenum type,
|
|
const void *data)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(
|
|
GL.glClearBufferSubData(target, internalformat, offset, size, format, type, data));
|
|
|
|
if(IsCaptureMode(m_State))
|
|
{
|
|
GLResourceRecord *record = GetCtxData().m_BufferRecord[BufferIdx(target)];
|
|
RDCASSERTMSG("Couldn't identify implicit object at binding. Mismatched or bad GLuint?", record,
|
|
target);
|
|
|
|
if(record)
|
|
{
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClearNamedBufferSubDataEXT(ser, record->Resource.name, internalformat, offset,
|
|
size, format, type, data);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GetResourceManager()->MarkDirtyResource(record->GetResourceID());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glClear(SerialiserType &ser, GLbitfield mask)
|
|
{
|
|
SERIALISE_ELEMENT_TYPED(GLframebufferbitfield, mask);
|
|
|
|
Serialise_DebugMessages(ser);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glClear(mask);
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
std::string name = ToStr(gl_CurChunk) + "(";
|
|
if(mask & GL_COLOR_BUFFER_BIT)
|
|
{
|
|
float col[4] = {0};
|
|
GL.glGetFloatv(eGL_COLOR_CLEAR_VALUE, &col[0]);
|
|
name += StringFormat::Fmt("Color = <%f, %f, %f, %f>, ", col[0], col[1], col[2], col[3]);
|
|
}
|
|
if(mask & GL_DEPTH_BUFFER_BIT)
|
|
{
|
|
float depth = 0;
|
|
GL.glGetFloatv(eGL_DEPTH_CLEAR_VALUE, &depth);
|
|
name += StringFormat::Fmt("Depth = <%f>, ", depth);
|
|
}
|
|
if(mask & GL_STENCIL_BUFFER_BIT)
|
|
{
|
|
GLint stencil = 0;
|
|
GL.glGetIntegerv(eGL_STENCIL_CLEAR_VALUE, &stencil);
|
|
name += StringFormat::Fmt("Stencil = <0x%02x>, ", stencil);
|
|
}
|
|
|
|
if(mask & (eGL_DEPTH_BUFFER_BIT | eGL_COLOR_BUFFER_BIT | eGL_STENCIL_BUFFER_BIT))
|
|
{
|
|
name.pop_back(); // ','
|
|
name.pop_back(); // ' '
|
|
}
|
|
|
|
name += ")";
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = name;
|
|
draw.flags |= DrawFlags::Clear;
|
|
if(mask & GL_COLOR_BUFFER_BIT)
|
|
draw.flags |= DrawFlags::ClearColor;
|
|
if(mask & (eGL_DEPTH_BUFFER_BIT | eGL_STENCIL_BUFFER_BIT))
|
|
draw.flags |= DrawFlags::ClearDepthStencil;
|
|
|
|
AddDrawcall(draw, true);
|
|
|
|
GLuint attachment = 0;
|
|
GLenum type = eGL_TEXTURE;
|
|
|
|
if(mask & GL_DEPTH_BUFFER_BIT)
|
|
{
|
|
GL.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
|
|
(GLint *)&attachment);
|
|
GL.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
|
|
(GLint *)&type);
|
|
|
|
if(attachment)
|
|
{
|
|
if(type == eGL_TEXTURE)
|
|
m_ResourceUses[GetResourceManager()->GetID(TextureRes(GetCtx(), attachment))].push_back(
|
|
EventUsage(m_CurEventID, ResourceUsage::Clear));
|
|
else
|
|
m_ResourceUses[GetResourceManager()->GetID(RenderbufferRes(GetCtx(), attachment))].push_back(
|
|
EventUsage(m_CurEventID, ResourceUsage::Clear));
|
|
}
|
|
}
|
|
|
|
attachment = 0;
|
|
type = eGL_TEXTURE;
|
|
|
|
if(mask & GL_STENCIL_BUFFER_BIT)
|
|
{
|
|
GL.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
|
|
(GLint *)&attachment);
|
|
GL.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
|
|
(GLint *)&type);
|
|
|
|
if(attachment)
|
|
{
|
|
if(type == eGL_TEXTURE)
|
|
m_ResourceUses[GetResourceManager()->GetID(TextureRes(GetCtx(), attachment))].push_back(
|
|
EventUsage(m_CurEventID, ResourceUsage::Clear));
|
|
else
|
|
m_ResourceUses[GetResourceManager()->GetID(RenderbufferRes(GetCtx(), attachment))].push_back(
|
|
EventUsage(m_CurEventID, ResourceUsage::Clear));
|
|
}
|
|
}
|
|
|
|
if(mask & GL_COLOR_BUFFER_BIT)
|
|
{
|
|
GLint numCols = 8;
|
|
GL.glGetIntegerv(eGL_MAX_COLOR_ATTACHMENTS, &numCols);
|
|
|
|
for(int i = 0; i < numCols; i++)
|
|
{
|
|
attachment = 0;
|
|
type = eGL_TEXTURE;
|
|
|
|
GL.glGetFramebufferAttachmentParameteriv(
|
|
eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + i),
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint *)&attachment);
|
|
GL.glGetFramebufferAttachmentParameteriv(
|
|
eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + i),
|
|
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint *)&type);
|
|
|
|
if(attachment)
|
|
{
|
|
if(type == eGL_TEXTURE)
|
|
m_ResourceUses[GetResourceManager()->GetID(TextureRes(GetCtx(), attachment))].push_back(
|
|
EventUsage(m_CurEventID, ResourceUsage::Clear));
|
|
else
|
|
m_ResourceUses[GetResourceManager()->GetID(RenderbufferRes(GetCtx(), attachment))]
|
|
.push_back(EventUsage(m_CurEventID, ResourceUsage::Clear));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glClear(GLbitfield mask)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glClear(mask));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClear(ser, mask);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glClearTexImage(SerialiserType &ser, GLuint textureHandle, GLint level,
|
|
GLenum format, GLenum type, const void *dataPtr)
|
|
{
|
|
SERIALISE_ELEMENT_LOCAL(texture, TextureRes(GetCtx(), textureHandle));
|
|
SERIALISE_ELEMENT(level);
|
|
SERIALISE_ELEMENT(format);
|
|
SERIALISE_ELEMENT(type);
|
|
|
|
uint64_t data[4] = {0};
|
|
|
|
if(ser.IsWriting())
|
|
{
|
|
size_t s = 1;
|
|
switch(format)
|
|
{
|
|
default:
|
|
RDCWARN("Unexpected format %x, defaulting to single component", format);
|
|
// fall through
|
|
case eGL_RED:
|
|
case eGL_RED_INTEGER:
|
|
case eGL_GREEN_INTEGER:
|
|
case eGL_BLUE_INTEGER:
|
|
case eGL_DEPTH_COMPONENT:
|
|
case eGL_STENCIL_INDEX: s = 1; break;
|
|
case eGL_RG:
|
|
case eGL_RG_INTEGER:
|
|
case eGL_DEPTH_STENCIL: s = 2; break;
|
|
case eGL_RGB:
|
|
case eGL_BGR:
|
|
case eGL_RGB_INTEGER:
|
|
case eGL_BGR_INTEGER: s = 3; break;
|
|
case eGL_RGBA:
|
|
case eGL_BGRA:
|
|
case eGL_RGBA_INTEGER:
|
|
case eGL_BGRA_INTEGER: s = 4; break;
|
|
}
|
|
switch(type)
|
|
{
|
|
case eGL_UNSIGNED_BYTE:
|
|
case eGL_BYTE: s *= 1; break;
|
|
case eGL_UNSIGNED_SHORT:
|
|
case eGL_SHORT: s *= 2; break;
|
|
case eGL_UNSIGNED_INT:
|
|
case eGL_INT:
|
|
case eGL_FLOAT: s *= 4; break;
|
|
default:
|
|
RDCWARN("Unexpected type %x, defaulting to 1 byte type", type);
|
|
// fall through
|
|
case eGL_UNSIGNED_BYTE_3_3_2:
|
|
case eGL_UNSIGNED_BYTE_2_3_3_REV: s = 1; break;
|
|
case eGL_UNSIGNED_SHORT_5_6_5:
|
|
case eGL_UNSIGNED_SHORT_5_6_5_REV:
|
|
case eGL_UNSIGNED_SHORT_4_4_4_4:
|
|
case eGL_UNSIGNED_SHORT_4_4_4_4_REV:
|
|
case eGL_UNSIGNED_SHORT_5_5_5_1:
|
|
case eGL_UNSIGNED_SHORT_1_5_5_5_REV:
|
|
case eGL_UNSIGNED_INT_8_8_8_8:
|
|
case eGL_UNSIGNED_INT_8_8_8_8_REV: s = 2; break;
|
|
case eGL_UNSIGNED_INT_10_10_10_2:
|
|
case eGL_UNSIGNED_INT_2_10_10_10_REV: s = 4; break;
|
|
}
|
|
if(dataPtr)
|
|
memcpy(data, dataPtr, s);
|
|
else
|
|
memset(data, 0, s);
|
|
}
|
|
|
|
SERIALISE_ELEMENT(data);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glClearTexImage(texture.name, level, format, type, (const void *)&data[0]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glClearTexImage(GLuint texture, GLint level, GLenum format, GLenum type,
|
|
const void *data)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glClearTexImage(texture, level, format, type, data));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClearTexImage(ser, texture, level, format, type, data);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
m_MissingTracks.insert(GetResourceManager()->GetID(TextureRes(GetCtx(), texture)));
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GetResourceManager()->MarkDirtyResource(TextureRes(GetCtx(), texture));
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glClearTexSubImage(SerialiserType &ser, GLuint textureHandle,
|
|
GLint level, GLint xoffset, GLint yoffset,
|
|
GLint zoffset, GLsizei width, GLsizei height,
|
|
GLsizei depth, GLenum format, GLenum type,
|
|
const void *dataPtr)
|
|
{
|
|
SERIALISE_ELEMENT_LOCAL(texture, TextureRes(GetCtx(), textureHandle));
|
|
SERIALISE_ELEMENT(level);
|
|
SERIALISE_ELEMENT(xoffset);
|
|
SERIALISE_ELEMENT(yoffset);
|
|
SERIALISE_ELEMENT(zoffset);
|
|
SERIALISE_ELEMENT(width);
|
|
SERIALISE_ELEMENT(height);
|
|
SERIALISE_ELEMENT(depth);
|
|
SERIALISE_ELEMENT(format);
|
|
SERIALISE_ELEMENT(type);
|
|
|
|
uint64_t data[4] = {0};
|
|
|
|
if(ser.IsWriting())
|
|
{
|
|
size_t s = 1;
|
|
switch(format)
|
|
{
|
|
default:
|
|
RDCWARN("Unexpected format %x, defaulting to single component", format);
|
|
// fall through
|
|
case eGL_RED:
|
|
case eGL_RED_INTEGER:
|
|
case eGL_GREEN_INTEGER:
|
|
case eGL_BLUE_INTEGER:
|
|
case eGL_DEPTH_COMPONENT:
|
|
case eGL_STENCIL_INDEX: s = 1; break;
|
|
case eGL_RG:
|
|
case eGL_RG_INTEGER:
|
|
case eGL_DEPTH_STENCIL: s = 2; break;
|
|
case eGL_RGB:
|
|
case eGL_BGR:
|
|
case eGL_RGB_INTEGER:
|
|
case eGL_BGR_INTEGER: s = 3; break;
|
|
case eGL_RGBA:
|
|
case eGL_BGRA:
|
|
case eGL_RGBA_INTEGER:
|
|
case eGL_BGRA_INTEGER: s = 4; break;
|
|
}
|
|
switch(type)
|
|
{
|
|
case eGL_UNSIGNED_BYTE:
|
|
case eGL_BYTE: s *= 1; break;
|
|
case eGL_UNSIGNED_SHORT:
|
|
case eGL_SHORT: s *= 2; break;
|
|
case eGL_UNSIGNED_INT:
|
|
case eGL_INT:
|
|
case eGL_FLOAT: s *= 4; break;
|
|
default:
|
|
RDCWARN("Unexpected type %x, defaulting to 1 byte type", type);
|
|
// fall through
|
|
case eGL_UNSIGNED_BYTE_3_3_2:
|
|
case eGL_UNSIGNED_BYTE_2_3_3_REV: s = 1; break;
|
|
case eGL_UNSIGNED_SHORT_5_6_5:
|
|
case eGL_UNSIGNED_SHORT_5_6_5_REV:
|
|
case eGL_UNSIGNED_SHORT_4_4_4_4:
|
|
case eGL_UNSIGNED_SHORT_4_4_4_4_REV:
|
|
case eGL_UNSIGNED_SHORT_5_5_5_1:
|
|
case eGL_UNSIGNED_SHORT_1_5_5_5_REV:
|
|
case eGL_UNSIGNED_INT_8_8_8_8:
|
|
case eGL_UNSIGNED_INT_8_8_8_8_REV: s = 2; break;
|
|
case eGL_UNSIGNED_INT_10_10_10_2:
|
|
case eGL_UNSIGNED_INT_2_10_10_10_REV: s = 4; break;
|
|
}
|
|
if(dataPtr)
|
|
memcpy(data, dataPtr, s);
|
|
else
|
|
memset(data, 0, s);
|
|
}
|
|
|
|
SERIALISE_ELEMENT(data);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glClearTexSubImage(texture.name, level, xoffset, yoffset, zoffset, width, height, depth,
|
|
format, type, (const void *)&data[0]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glClearTexSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset,
|
|
GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
|
|
GLenum format, GLenum type, const void *data)
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glClearTexSubImage(texture, level, xoffset, yoffset, zoffset, width,
|
|
height, depth, format, type, data));
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glClearTexSubImage(ser, texture, level, xoffset, yoffset, zoffset, width, height,
|
|
depth, format, type, data);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
m_MissingTracks.insert(GetResourceManager()->GetID(TextureRes(GetCtx(), texture)));
|
|
}
|
|
else if(IsBackgroundCapturing(m_State))
|
|
{
|
|
GetResourceManager()->MarkDirtyResource(TextureRes(GetCtx(), texture));
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glFlush(SerialiserType &ser)
|
|
{
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glFlush();
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = ToStr(gl_CurChunk) + "()";
|
|
draw.flags |= DrawFlags::SetMarker;
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glFlush()
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glFlush());
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glFlush(ser);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
bool WrappedOpenGL::Serialise_glFinish(SerialiserType &ser)
|
|
{
|
|
if(IsReplayingAndReading())
|
|
{
|
|
GL.glFinish();
|
|
|
|
if(IsLoading(m_State))
|
|
{
|
|
AddEvent();
|
|
|
|
DrawcallDescription draw;
|
|
draw.name = ToStr(gl_CurChunk) + "()";
|
|
draw.flags |= DrawFlags::SetMarker;
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::glFinish()
|
|
{
|
|
CoherentMapImplicitBarrier();
|
|
|
|
SERIALISE_TIME_CALL(GL.glFinish());
|
|
|
|
if(IsActiveCapturing(m_State))
|
|
{
|
|
USE_SCRATCH_SERIALISER();
|
|
|
|
ser.SetDrawChunk();
|
|
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
|
|
Serialise_glFinish(ser);
|
|
|
|
GetContextRecord()->AddChunk(scope.Get());
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDispatchCompute, GLuint num_groups_x, GLuint num_groups_y,
|
|
GLuint num_groups_z);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDispatchComputeGroupSizeARB, GLuint num_groups_x,
|
|
GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x,
|
|
GLuint group_size_y, GLuint group_size_z);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDispatchComputeIndirect, GLintptr indirect);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glMemoryBarrier, GLbitfield barriers);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glMemoryBarrierByRegion, GLbitfield barriers);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glTextureBarrier);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawTransformFeedback, GLenum mode, GLuint xfbHandle);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawTransformFeedbackInstanced, GLenum mode, GLuint id,
|
|
GLsizei instancecount);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawTransformFeedbackStream, GLenum mode, GLuint id,
|
|
GLuint stream);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawTransformFeedbackStreamInstanced, GLenum mode,
|
|
GLuint id, GLuint stream, GLsizei instancecount);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawArrays, GLenum mode, GLint first, GLsizei count);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawArraysIndirect, GLenum mode, const void *indirect);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawArraysInstanced, GLenum mode, GLint first,
|
|
GLsizei count, GLsizei instancecount);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawArraysInstancedBaseInstance, GLenum mode, GLint first,
|
|
GLsizei count, GLsizei instancecount, GLuint baseinstance);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawElements, GLenum mode, GLsizei count, GLenum type,
|
|
const void *indicesPtr);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawElementsIndirect, GLenum mode, GLenum type,
|
|
const void *indirect);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawRangeElements, GLenum mode, GLuint start, GLuint end,
|
|
GLsizei count, GLenum type, const void *indicesPtr);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawRangeElementsBaseVertex, GLenum mode, GLuint start,
|
|
GLuint end, GLsizei count, GLenum type, const void *indicesPtr,
|
|
GLint basevertex);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawElementsBaseVertex, GLenum mode, GLsizei count,
|
|
GLenum type, const void *indices, GLint basevertex);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawElementsInstanced, GLenum mode, GLsizei count,
|
|
GLenum type, const void *indices, GLsizei instancecount);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawElementsInstancedBaseInstance, GLenum mode,
|
|
GLsizei count, GLenum type, const void *indicesPtr,
|
|
GLsizei instancecount, GLuint baseinstance);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawElementsInstancedBaseVertex, GLenum mode, GLsizei count,
|
|
GLenum type, const void *indicesPtr, GLsizei instancecount,
|
|
GLint basevertex);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glDrawElementsInstancedBaseVertexBaseInstance, GLenum mode,
|
|
GLsizei count, GLenum type, const void *indices,
|
|
GLsizei instancecount, GLint basevertex, GLuint baseinstance);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glMultiDrawArrays, GLenum mode, const GLint *first,
|
|
const GLsizei *count, GLsizei drawcount);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glMultiDrawElements, GLenum mode, const GLsizei *count,
|
|
GLenum type, const void *const *indices, GLsizei drawcount);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glMultiDrawElementsBaseVertex, GLenum mode,
|
|
const GLsizei *count, GLenum type, const void *const *indices,
|
|
GLsizei drawcount, const GLint *basevertex);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glMultiDrawArraysIndirect, GLenum mode, const void *indirect,
|
|
GLsizei drawcount, GLsizei stride);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glMultiDrawElementsIndirect, GLenum mode, GLenum type,
|
|
const void *indirect, GLsizei drawcount, GLsizei stride);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glMultiDrawArraysIndirectCount, GLenum mode,
|
|
const void *indirect, GLintptr drawcount, GLsizei maxdrawcount,
|
|
GLsizei stride);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glMultiDrawElementsIndirectCount, GLenum mode, GLenum type,
|
|
const void *indirect, GLintptr drawcount, GLsizei maxdrawcount,
|
|
GLsizei stride);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glClearNamedFramebufferfv, GLuint framebufferHandle,
|
|
GLenum buffer, GLint drawbuffer, const GLfloat *valuePtr);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glClearNamedFramebufferiv, GLuint framebuffer, GLenum buffer,
|
|
GLint drawbuffer, const GLint *valuePtr);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glClearNamedFramebufferuiv, GLuint framebuffer, GLenum buffer,
|
|
GLint drawbuffer, const GLuint *value);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glClearNamedFramebufferfi, GLuint framebuffer, GLenum buffer,
|
|
GLint drawbuffer, GLfloat depth, GLint stencil);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glClearNamedBufferDataEXT, GLuint bufferHandle,
|
|
GLenum internalformat, GLenum format, GLenum type,
|
|
const void *dataPtr);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glClearNamedBufferSubDataEXT, GLuint buffer,
|
|
GLenum internalformat, GLintptr offsetPtr, GLsizeiptr sizePtr,
|
|
GLenum format, GLenum type, const void *dataPtr);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glClear, GLbitfield mask);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glClearTexImage, GLuint texture, GLint level, GLenum format,
|
|
GLenum type, const void *dataPtr);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glClearTexSubImage, GLuint texture, GLint level, GLint xoffset,
|
|
GLint yoffset, GLint zoffset, GLsizei width, GLsizei height,
|
|
GLsizei depth, GLenum format, GLenum type, const void *dataPtr);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glFlush);
|
|
INSTANTIATE_FUNCTION_SERIALISED(void, glFinish);
|