Allow glDrawElements and friends to pass indices as memory. Closes #127

* This is a concession towards non-core capturing, as it's fairly easy
  to support this fallback case without having a negative effect on the
  proper path.
* We serialise out whether an index buffer was bound, and if it wasn't
  we serialise the array passed as a parameter, and upload it into a
  temporary index buffer we generate (so that the replay is correct at
  least).
* I think technically the indirect parameter for glDrawElementsIndirect
  can point to client memory, and the elements array-of-arrays in
  glMultiDrawElements can definitely point to client memory, but I'm
  assuming that no-one would do that. I certainly hope they wouldn't!
This commit is contained in:
baldurk
2015-04-10 19:05:21 +01:00
parent 95f4011bcb
commit 3ca65bd632
3 changed files with 126 additions and 0 deletions
+15
View File
@@ -787,6 +787,14 @@ void WrappedOpenGL::Initialise(GLInitParams &params)
gl.glBindVertexArray(m_FakeVAO);
gl.glBindVertexArray(0);
// we use this to draw from index data that was 'immediate' passed to the
// draw function, as i na real memory pointer
gl.glGenBuffers(1, &m_FakeIdxBuf);
gl.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, m_FakeIdxBuf);
m_FakeIdxSize = 1024*1024; // this buffer is resized up as needed
gl.glNamedBufferStorageEXT(m_FakeIdxBuf, m_FakeIdxSize, NULL, GL_DYNAMIC_STORAGE_BIT);
gl.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, 0);
gl.glGenFramebuffers(1, &m_FakeBB_FBO);
gl.glBindFramebuffer(eGL_FRAMEBUFFER, m_FakeBB_FBO);
@@ -825,6 +833,7 @@ void WrappedOpenGL::Initialise(GLInitParams &params)
gl.glViewport(0, 0, params.width, params.height);
m_FakeBB_DepthStencil = 0;
if(params.depthBits > 0 || params.stencilBits > 0)
{
gl.glGenTextures(1, &m_FakeBB_DepthStencil);
@@ -884,6 +893,12 @@ const char * WrappedOpenGL::GetChunkName(uint32_t idx)
WrappedOpenGL::~WrappedOpenGL()
{
m_Real.glDeleteBuffers(1, &m_FakeIdxBuf);
m_Real.glDeleteVertexArrays(1, &m_FakeVAO);
m_Real.glDeleteFramebuffers(1, &m_FakeBB_FBO);
m_Real.glDeleteTextures(1, &m_FakeBB_Color);
if(m_FakeBB_DepthStencil) m_Real.glDeleteTextures(1, &m_FakeBB_DepthStencil);
SAFE_DELETE(m_pSerialiser);
GetResourceManager()->ReleaseCurrentResource(m_DeviceResourceID);
+8
View File
@@ -286,6 +286,8 @@ class WrappedOpenGL
GLuint m_FakeBB_Color;
GLuint m_FakeBB_DepthStencil;
GLuint m_FakeVAO;
GLuint m_FakeIdxBuf;
GLsizeiptr m_FakeIdxSize;
ResourceId m_FakeVAOID;
@@ -1113,6 +1115,12 @@ class WrappedOpenGL
IMPLEMENT_FUNCTION_SERIALISED(void, glProgramUniformMatrix4x2dv(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value));
IMPLEMENT_FUNCTION_SERIALISED(void, glProgramUniformMatrix4x3dv(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value));
// utility handling functions for glDraw*Elements* to handle pointers to indices being
// passed directly, with no index buffer bound. It's not allowed in core profile but
// it's fairly common and not too hard to support
byte *Common_preElements(GLsizei Count, GLenum Type, uint64_t &IdxOffset);
void Common_postElements(byte *idxDelete);
IMPLEMENT_FUNCTION_SERIALISED(void, glDrawArrays(GLenum mode, GLint first, GLsizei count));
IMPLEMENT_FUNCTION_SERIALISED(void, glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount));
IMPLEMENT_FUNCTION_SERIALISED(void, glDrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance));
@@ -724,16 +724,91 @@ void WrappedOpenGL::glDrawArraysInstancedBaseInstance(GLenum mode, GLint first,
}
}
byte *WrappedOpenGL::Common_preElements(GLsizei Count, GLenum Type, uint64_t &IdxOffset)
{
GLint idxbuf = 0;
// while writing, check to see if an index buffer is bound
if(m_State >= WRITING)
m_Real.glGetIntegerv(eGL_ELEMENT_ARRAY_BUFFER_BINDING, &idxbuf);
// serialise whether we're reading indices as memory
SERIALISE_ELEMENT(bool, IndicesFromMemory, idxbuf == 0);
if(IndicesFromMemory)
{
uint32_t IdxSize =
Type == eGL_UNSIGNED_BYTE ? 1
: Type == eGL_UNSIGNED_SHORT ? 2
: /*Type == eGL_UNSIGNED_INT*/ 4;
// serialise the actual data (IdxOffset is a pointer not an offset in this case)
SERIALISE_ELEMENT_BUF(byte *, idxdata, (void *)IdxOffset, size_t(IdxSize*Count));
if(m_State <= EXECUTING)
{
GLsizeiptr idxlen = GLsizeiptr(IdxSize*Count);
// resize fake index buffer if necessary
if(idxlen > m_FakeIdxSize)
{
m_Real.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, 0);
m_Real.glDeleteBuffers(1, &m_FakeIdxBuf);
m_FakeIdxSize = idxlen;
m_Real.glGenBuffers(1, &m_FakeIdxBuf);
m_Real.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, m_FakeIdxBuf);
m_Real.glNamedBufferStorageEXT(m_FakeIdxBuf, m_FakeIdxSize, NULL, GL_DYNAMIC_STORAGE_BIT);
}
// bind and update fake index buffer, to draw from the 'immediate' index data
m_Real.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, m_FakeIdxBuf);
m_Real.glNamedBufferSubDataEXT(m_FakeIdxBuf, 0, idxlen, idxdata);
// Set offset to 0 - means we read data from start of our fake index buffer
IdxOffset = 0;
// we'll delete this later (only when replaying)
return idxdata;
}
// can just return NULL, since we don't need to do any cleanup or deletion
}
return NULL;
}
void WrappedOpenGL::Common_postElements(byte *idxDelete)
{
// unbind temporary fake index buffer we used to pass 'immediate' index data
if(idxDelete)
{
m_Real.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, 0);
AddDebugMessage(eDbgCategory_Deprecated, eDbgSeverity_High, eDbgSource_IncorrectAPIUse,
"Assuming GL core profile is used then specifying indices as a raw array, "
"not as offset into element array buffer, is illegal.");
// delete serialised data
SAFE_DELETE_ARRAY(idxDelete);
}
}
bool WrappedOpenGL::Serialise_glDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices)
{
SERIALISE_ELEMENT(GLenum, Mode, mode);
SERIALISE_ELEMENT(uint32_t, Count, count);
SERIALISE_ELEMENT(GLenum, Type, type);
SERIALISE_ELEMENT(uint64_t, IdxOffset, (uint64_t)indices);
byte *idxDelete = Common_preElements(Count, Type, IdxOffset);
if(m_State <= EXECUTING)
{
m_Real.glDrawElements(Mode, Count, Type, (const void *)IdxOffset);
Common_postElements(idxDelete);
}
const string desc = m_pSerialiser->GetDebugStr();
@@ -864,10 +939,14 @@ bool WrappedOpenGL::Serialise_glDrawRangeElements(GLenum mode, GLuint start, GLu
SERIALISE_ELEMENT(uint32_t, Count, count);
SERIALISE_ELEMENT(GLenum, Type, type);
SERIALISE_ELEMENT(uint64_t, IdxOffset, (uint64_t)indices);
byte *idxDelete = Common_preElements(Count, Type, IdxOffset);
if(m_State <= EXECUTING)
{
m_Real.glDrawRangeElements(Mode, Start, End, Count, Type, (const void *)IdxOffset);
Common_postElements(idxDelete);
}
const string desc = m_pSerialiser->GetDebugStr();
@@ -932,9 +1011,13 @@ bool WrappedOpenGL::Serialise_glDrawRangeElementsBaseVertex(GLenum mode, GLuint
SERIALISE_ELEMENT(uint64_t, IdxOffset, (uint64_t)indices);
SERIALISE_ELEMENT(uint32_t, BaseVtx, basevertex);
byte *idxDelete = Common_preElements(Count, Type, IdxOffset);
if(m_State <= EXECUTING)
{
m_Real.glDrawRangeElementsBaseVertex(Mode, Start, End, Count, Type, (const void *)IdxOffset, BaseVtx);
Common_postElements(idxDelete);
}
const string desc = m_pSerialiser->GetDebugStr();
@@ -997,10 +1080,14 @@ bool WrappedOpenGL::Serialise_glDrawElementsBaseVertex(GLenum mode, GLsizei coun
SERIALISE_ELEMENT(GLenum, Type, type);
SERIALISE_ELEMENT(uint64_t, IdxOffset, (uint64_t)indices);
SERIALISE_ELEMENT(int32_t, BaseVtx, basevertex);
byte *idxDelete = Common_preElements(Count, Type, IdxOffset);
if(m_State <= EXECUTING)
{
m_Real.glDrawElementsBaseVertex(Mode, Count, Type, (const void *)IdxOffset, BaseVtx);
Common_postElements(idxDelete);
}
const string desc = m_pSerialiser->GetDebugStr();
@@ -1064,9 +1151,13 @@ bool WrappedOpenGL::Serialise_glDrawElementsInstanced(GLenum mode, GLsizei count
SERIALISE_ELEMENT(uint64_t, IdxOffset, (uint64_t)indices);
SERIALISE_ELEMENT(uint32_t, InstCount, instancecount);
byte *idxDelete = Common_preElements(Count, Type, IdxOffset);
if(m_State <= EXECUTING)
{
m_Real.glDrawElementsInstanced(Mode, Count, Type, (const void *)IdxOffset, InstCount);
Common_postElements(idxDelete);
}
const string desc = m_pSerialiser->GetDebugStr();
@@ -1130,10 +1221,14 @@ bool WrappedOpenGL::Serialise_glDrawElementsInstancedBaseInstance(GLenum mode, G
SERIALISE_ELEMENT(uint64_t, IdxOffset, (uint64_t)indices);
SERIALISE_ELEMENT(uint32_t, InstCount, instancecount);
SERIALISE_ELEMENT(uint32_t, BaseInstance, baseinstance);
byte *idxDelete = Common_preElements(Count, Type, IdxOffset);
if(m_State <= EXECUTING)
{
m_Real.glDrawElementsInstancedBaseInstance(Mode, Count, Type, (const void *)IdxOffset, InstCount, BaseInstance);
Common_postElements(idxDelete);
}
const string desc = m_pSerialiser->GetDebugStr();
@@ -1199,9 +1294,13 @@ bool WrappedOpenGL::Serialise_glDrawElementsInstancedBaseVertex(GLenum mode, GLs
SERIALISE_ELEMENT(uint32_t, InstCount, instancecount);
SERIALISE_ELEMENT(int32_t, BaseVertex, basevertex);
byte *idxDelete = Common_preElements(Count, Type, IdxOffset);
if(m_State <= EXECUTING)
{
m_Real.glDrawElementsInstancedBaseVertex(Mode, Count, Type, (const void *)IdxOffset, InstCount, BaseVertex);
Common_postElements(idxDelete);
}
const string desc = m_pSerialiser->GetDebugStr();
@@ -1267,10 +1366,14 @@ bool WrappedOpenGL::Serialise_glDrawElementsInstancedBaseVertexBaseInstance(GLen
SERIALISE_ELEMENT(uint32_t, InstCount, instancecount);
SERIALISE_ELEMENT(int32_t, BaseVertex, basevertex);
SERIALISE_ELEMENT(uint32_t, BaseInstance, baseinstance);
byte *idxDelete = Common_preElements(Count, Type, IdxOffset);
if(m_State <= EXECUTING)
{
m_Real.glDrawElementsInstancedBaseVertexBaseInstance(Mode, Count, Type, (const void *)IdxOffset, InstCount, BaseVertex, BaseInstance);
Common_postElements(idxDelete);
}
const string desc = m_pSerialiser->GetDebugStr();