/****************************************************************************** * The MIT License (MIT) * * Copyright (c) 2014 Crytek * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ #include "common/common.h" #include "serialise/string_utils.h" #include "../gl_driver.h" #pragma region Buffers bool WrappedOpenGL::Serialise_glGenBuffers(GLsizei n, GLuint* textures) { SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(BufferRes(GetCtx(), *textures))); if(m_State == READING) { GLuint real = 0; m_Real.glGenBuffers(1, &real); GLResource res = BufferRes(GetCtx(), real); ResourceId live = m_ResourceManager->RegisterResource(res); GetResourceManager()->AddLiveResource(id, res); m_Buffers[live].resource = res; m_Buffers[live].curType = eGL_NONE; } return true; } void WrappedOpenGL::glGenBuffers(GLsizei n, GLuint *buffers) { m_Real.glGenBuffers(n, buffers); for(GLsizei i=0; i < n; i++) { GLResource res = BufferRes(GetCtx(), buffers[i]); ResourceId id = GetResourceManager()->RegisterResource(res); if(m_State >= WRITING) { Chunk *chunk = NULL; { SCOPED_SERIALISE_CONTEXT(GEN_BUFFER); Serialise_glGenBuffers(1, buffers+i); chunk = scope.Get(); } GLResourceRecord *record = GetResourceManager()->AddResourceRecord(id); RDCASSERT(record); record->AddChunk(chunk); } else { GetResourceManager()->AddLiveResource(id, res); } } } bool WrappedOpenGL::Serialise_glBindBuffer(GLenum target, GLuint buffer) { SERIALISE_ELEMENT(GLenum, Target, target); SERIALISE_ELEMENT(ResourceId, Id, (buffer ? GetResourceManager()->GetID(BufferRes(GetCtx(), buffer)) : ResourceId())); if(m_State >= WRITING) { size_t idx = BufferIdx(target); if(buffer != 0) m_BufferRecord[idx]->datatype = Target; } else if(m_State < WRITING) { if(Id == ResourceId()) { m_Real.glBindBuffer(Target, 0); } else { GLResource res = GetResourceManager()->GetLiveResource(Id); m_Real.glBindBuffer(Target, res.name); m_Buffers[GetResourceManager()->GetLiveID(Id)].curType = Target; } } return true; } void WrappedOpenGL::glBindBuffer(GLenum target, GLuint buffer) { m_Real.glBindBuffer(target, buffer); size_t idx = BufferIdx(target); if(m_State == WRITING_CAPFRAME) { Chunk *chunk = NULL; if(buffer == 0) m_BufferRecord[idx] = NULL; else m_BufferRecord[idx] = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), buffer)); { SCOPED_SERIALISE_CONTEXT(BIND_BUFFER); Serialise_glBindBuffer(target, buffer); chunk = scope.Get(); } m_ContextRecord->AddChunk(chunk); } if(buffer == 0) { m_BufferRecord[idx] = NULL; return; } if(m_State >= WRITING) { GLResourceRecord *r = m_BufferRecord[idx] = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), buffer)); // it's legal to re-type buffers, generate another BindBuffer chunk to rename if(r->datatype != target) { Chunk *chunk = NULL; { SCOPED_SERIALISE_CONTEXT(BIND_BUFFER); Serialise_glBindBuffer(target, buffer); chunk = scope.Get(); } r->AddChunk(chunk); } // element array buffer binding is vertex array record state, record there (if we've not just stopped) if(m_State == WRITING_IDLE && target == eGL_ELEMENT_ARRAY_BUFFER && m_VertexArrayRecord && VertexArrayUpdateCheck()) { SCOPED_SERIALISE_CONTEXT(BIND_BUFFER); Serialise_glBindBuffer(target, buffer); m_VertexArrayRecord->AddChunk(scope.Get()); } } } bool WrappedOpenGL::Serialise_glNamedBufferStorageEXT(GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags) { SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(BufferRes(GetCtx(), buffer))); SERIALISE_ELEMENT(uint64_t, Bytesize, (uint64_t)size); byte *dummy = NULL; if(m_State >= WRITING && data == NULL) { dummy = new byte[size]; data = dummy; } SERIALISE_ELEMENT_BUF(byte *, bytes, data, (size_t)Bytesize); uint64_t offs = m_pSerialiser->GetOffset(); SERIALISE_ELEMENT(uint32_t, Flags, flags); if(m_State < WRITING) { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glNamedBufferStorageEXT(res.name, (GLsizeiptr)Bytesize, bytes, Flags); m_Buffers[GetResourceManager()->GetLiveID(id)].size = Bytesize; SAFE_DELETE_ARRAY(bytes); } else if(m_State >= WRITING) { GetResourceManager()->GetResourceRecord(id)->SetDataOffset(offs - Bytesize); } if(dummy) delete[] dummy; return true; } void WrappedOpenGL::glNamedBufferStorageEXT(GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags) { m_Real.glNamedBufferStorageEXT(buffer, size, data, flags); if(m_State >= WRITING) { GLResourceRecord *record = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), buffer)); RDCASSERT(record); SCOPED_SERIALISE_CONTEXT(BUFFERSTORAGE); Serialise_glNamedBufferStorageEXT(buffer, size, data, flags); Chunk *chunk = scope.Get(); if(m_State == WRITING_CAPFRAME) { m_ContextRecord->AddChunk(chunk); } else { record->AddChunk(chunk); record->SetDataPtr(chunk->GetData()); record->Length = (int32_t)size; } } } void WrappedOpenGL::glBufferStorage(GLenum target, GLsizeiptr size, const void *data, GLbitfield flags) { m_Real.glBufferStorage(target, size, data, flags); size_t idx = BufferIdx(target); if(m_State >= WRITING) { GLResourceRecord *record = m_BufferRecord[BufferIdx(target)]; RDCASSERT(record); SCOPED_SERIALISE_CONTEXT(BUFFERSTORAGE); Serialise_glNamedBufferStorageEXT(GetResourceManager()->GetCurrentResource(record->GetResourceID()).name, size, data, flags); Chunk *chunk = scope.Get(); if(m_State == WRITING_CAPFRAME) { m_ContextRecord->AddChunk(chunk); } else { record->AddChunk(chunk); record->SetDataPtr(chunk->GetData()); record->Length = (int32_t)size; } } } bool WrappedOpenGL::Serialise_glNamedBufferDataEXT(GLuint buffer, GLsizeiptr size, const void *data, GLenum usage) { SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(BufferRes(GetCtx(), buffer))); SERIALISE_ELEMENT(uint64_t, Bytesize, (uint64_t)size); byte *dummy = NULL; if(m_State >= WRITING && data == NULL) { dummy = new byte[size]; data = dummy; } SERIALISE_ELEMENT_BUF(byte *, bytes, data, (size_t)Bytesize); uint64_t offs = m_pSerialiser->GetOffset(); SERIALISE_ELEMENT(GLenum, Usage, usage); if(m_State < WRITING) { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glNamedBufferDataEXT(res.name, (GLsizeiptr)Bytesize, bytes, Usage); m_Buffers[GetResourceManager()->GetLiveID(id)].size = Bytesize; SAFE_DELETE_ARRAY(bytes); } else if(m_State >= WRITING) { GetResourceManager()->GetResourceRecord(id)->SetDataOffset(offs - Bytesize); } if(dummy) delete[] dummy; return true; } void WrappedOpenGL::glNamedBufferDataEXT(GLuint buffer, GLsizeiptr size, const void *data, GLenum usage) { m_Real.glNamedBufferDataEXT(buffer, size, data, usage); if(m_State >= WRITING) { GLResourceRecord *record = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), buffer)); RDCASSERT(record); // detect buffer orphaning and just update backing store if(m_State == WRITING_IDLE && record->GetDataPtr() != NULL && size == record->Length && usage == record->usage) { if(data) memcpy(record->GetDataPtr(), data, (size_t)size); else memset(record->GetDataPtr(), 0xbe, (size_t)size); return; } SCOPED_SERIALISE_CONTEXT(BUFFERDATA); Serialise_glNamedBufferDataEXT(buffer, size, data, usage); Chunk *chunk = scope.Get(); if(m_State == WRITING_CAPFRAME) { m_ContextRecord->AddChunk(chunk); } else { record->AddChunk(chunk); record->SetDataPtr(chunk->GetData()); record->Length = (int32_t)size; record->usage = usage; } } } void WrappedOpenGL::glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage) { m_Real.glBufferData(target, size, data, usage); size_t idx = BufferIdx(target); if(m_State >= WRITING) { GLResourceRecord *record = m_BufferRecord[BufferIdx(target)]; RDCASSERT(record); // detect buffer orphaning and just update backing store if(m_State == WRITING_IDLE && record->GetDataPtr() != NULL && size == record->Length && usage == record->usage) { if(data) memcpy(record->GetDataPtr(), data, (size_t)size); else memset(record->GetDataPtr(), 0xbe, (size_t)size); return; } SCOPED_SERIALISE_CONTEXT(BUFFERDATA); Serialise_glNamedBufferDataEXT(GetResourceManager()->GetCurrentResource(record->GetResourceID()).name, size, data, usage); Chunk *chunk = scope.Get(); if(m_State == WRITING_CAPFRAME) { m_ContextRecord->AddChunk(chunk); } else { record->AddChunk(chunk); record->SetDataPtr(chunk->GetData()); record->Length = (int32_t)size; record->usage = usage; } } } bool WrappedOpenGL::Serialise_glNamedBufferSubDataEXT(GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data) { SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(BufferRes(GetCtx(), buffer))); SERIALISE_ELEMENT(uint64_t, Offset, (uint64_t)offset); SERIALISE_ELEMENT(uint64_t, Bytesize, (uint64_t)size); SERIALISE_ELEMENT_BUF(byte *, bytes, data, (size_t)Bytesize); if(m_State < WRITING) { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glNamedBufferSubDataEXT(res.name, (GLintptr)Offset, (GLsizeiptr)Bytesize, bytes); SAFE_DELETE_ARRAY(bytes); } return true; } void WrappedOpenGL::glNamedBufferSubDataEXT(GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data) { m_Real.glNamedBufferSubDataEXT(buffer, offset, size, data); if(m_State >= WRITING) { if(m_HighTrafficResources.find(BufferRes(GetCtx(), buffer)) != m_HighTrafficResources.end() && m_State != WRITING_CAPFRAME) return; GLResourceRecord *record = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), buffer)); RDCASSERT(record); SCOPED_SERIALISE_CONTEXT(BUFFERSUBDATA); Serialise_glNamedBufferSubDataEXT(buffer, offset, size, data); Chunk *chunk = scope.Get(); if(m_State == WRITING_CAPFRAME) { m_ContextRecord->AddChunk(chunk); } else { record->AddChunk(chunk); record->UpdateCount++; if(record->UpdateCount > 60) { m_HighTrafficResources.insert(BufferRes(GetCtx(), buffer)); GetResourceManager()->MarkDirtyResource(record->GetResourceID()); } } } } void WrappedOpenGL::glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void *data) { m_Real.glBufferSubData(target, offset, size, data); if(m_State >= WRITING) { GLResourceRecord *record = m_BufferRecord[BufferIdx(target)]; RDCASSERT(record); GLResource res = GetResourceManager()->GetCurrentResource(record->GetResourceID()); if(m_HighTrafficResources.find(res) != m_HighTrafficResources.end() && m_State != WRITING_CAPFRAME) return; SCOPED_SERIALISE_CONTEXT(BUFFERSUBDATA); Serialise_glNamedBufferSubDataEXT(res.name, offset, size, data); Chunk *chunk = scope.Get(); if(m_State == WRITING_CAPFRAME) m_ContextRecord->AddChunk(chunk); else { record->AddChunk(chunk); record->UpdateCount++; if(record->UpdateCount > 60) { m_HighTrafficResources.insert(res); GetResourceManager()->MarkDirtyResource(record->GetResourceID()); } } } } bool WrappedOpenGL::Serialise_glNamedCopyBufferSubDataEXT(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) { SERIALISE_ELEMENT(ResourceId, readid, GetResourceManager()->GetID(BufferRes(GetCtx(), readBuffer))); SERIALISE_ELEMENT(ResourceId, writeid, GetResourceManager()->GetID(BufferRes(GetCtx(), writeBuffer))); SERIALISE_ELEMENT(uint64_t, ReadOffset, (uint64_t)readOffset); SERIALISE_ELEMENT(uint64_t, WriteOffset, (uint64_t)writeOffset); SERIALISE_ELEMENT(uint64_t, Bytesize, (uint64_t)size); if(m_State < WRITING) { GLResource readres = GetResourceManager()->GetLiveResource(readid); GLResource writeres = GetResourceManager()->GetLiveResource(writeid); m_Real.glNamedCopyBufferSubDataEXT(readres.name, writeres.name, (GLintptr)ReadOffset, (GLintptr)WriteOffset, (GLsizeiptr)Bytesize); } return true; } void WrappedOpenGL::glNamedCopyBufferSubDataEXT(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) { m_Real.glNamedCopyBufferSubDataEXT(readBuffer, writeBuffer, readOffset, writeOffset, size); if(m_State >= WRITING) { GLResourceRecord *readrecord = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), readBuffer)); GLResourceRecord *writerecord = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), writeBuffer)); RDCASSERT(readrecord && writerecord); SCOPED_SERIALISE_CONTEXT(COPYBUFFERSUBDATA); Serialise_glNamedCopyBufferSubDataEXT(readBuffer, writeBuffer, readOffset, writeOffset, size); Chunk *chunk = scope.Get(); if(m_State == WRITING_CAPFRAME) { m_ContextRecord->AddChunk(chunk); } else { writerecord->AddChunk(chunk); writerecord->AddParent(readrecord); } } } void WrappedOpenGL::glCopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) { m_Real.glCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); if(m_State >= WRITING) { GLResourceRecord *readrecord = m_BufferRecord[BufferIdx(readTarget)]; GLResourceRecord *writerecord = m_BufferRecord[BufferIdx(writeTarget)]; RDCASSERT(readrecord && writerecord); SCOPED_SERIALISE_CONTEXT(COPYBUFFERSUBDATA); Serialise_glNamedCopyBufferSubDataEXT(GetResourceManager()->GetCurrentResource(readrecord->GetResourceID()).name, GetResourceManager()->GetCurrentResource(writerecord->GetResourceID()).name, readOffset, writeOffset, size); Chunk *chunk = scope.Get(); if(m_State == WRITING_CAPFRAME) { m_ContextRecord->AddChunk(chunk); } else { writerecord->AddChunk(chunk); writerecord->AddParent(readrecord); } } } bool WrappedOpenGL::Serialise_glBindBufferBase(GLenum target, GLuint index, GLuint buffer) { SERIALISE_ELEMENT(GLenum, Target, target); SERIALISE_ELEMENT(uint32_t, Index, index); SERIALISE_ELEMENT(ResourceId, id, (buffer ? GetResourceManager()->GetID(BufferRes(GetCtx(), buffer)) : ResourceId())); if(m_State < WRITING) { if(id == ResourceId()) { m_Real.glBindBuffer(Target, 0); } else { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glBindBufferBase(Target, Index, res.name); } } return true; } void WrappedOpenGL::glBindBufferBase(GLenum target, GLuint index, GLuint buffer) { if(m_State >= WRITING && index == 0) { size_t idx = BufferIdx(target); if(buffer == 0) m_BufferRecord[idx] = NULL; else m_BufferRecord[idx] = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), buffer)); } if(m_State == WRITING_CAPFRAME) { SCOPED_SERIALISE_CONTEXT(BIND_BUFFER_BASE); Serialise_glBindBufferBase(target, index, buffer); m_ContextRecord->AddChunk(scope.Get()); } m_Real.glBindBufferBase(target, index, buffer); } bool WrappedOpenGL::Serialise_glBindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) { SERIALISE_ELEMENT(GLenum, Target, target); SERIALISE_ELEMENT(uint32_t, Index, index); SERIALISE_ELEMENT(ResourceId, id, (buffer ? GetResourceManager()->GetID(BufferRes(GetCtx(), buffer)) : ResourceId())); SERIALISE_ELEMENT(uint64_t, Offset, (uint64_t)offset); SERIALISE_ELEMENT(uint64_t, Size, (uint64_t)size); if(m_State < WRITING) { if(id == ResourceId()) { m_Real.glBindBuffer(Target, 0); } else { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glBindBufferRange(Target, Index, res.name, (GLintptr)Offset, (GLsizeiptr)Size); } } return true; } void WrappedOpenGL::glBindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) { if(m_State >= WRITING && index == 0) { size_t idx = BufferIdx(target); if(buffer == 0) m_BufferRecord[idx] = NULL; else m_BufferRecord[idx] = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), buffer)); } if(m_State == WRITING_CAPFRAME) { SCOPED_SERIALISE_CONTEXT(BIND_BUFFER_RANGE); Serialise_glBindBufferRange(target, index, buffer, offset, size); m_ContextRecord->AddChunk(scope.Get()); } m_Real.glBindBufferRange(target, index, buffer, offset, size); } bool WrappedOpenGL::Serialise_glBindBuffersBase(GLenum target, GLuint first, GLsizei count, const GLuint *buffers) { SERIALISE_ELEMENT(GLenum, Target, target); SERIALISE_ELEMENT(uint32_t, First, first); SERIALISE_ELEMENT(int32_t, Count, count); GLuint *bufs = NULL; if(m_State <= EXECUTING) bufs = new GLuint[Count]; for(int32_t i=0; i < Count; i++) { SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(BufferRes(GetCtx(), buffers[i]))); if(m_State <= EXECUTING) { if(id != ResourceId()) bufs[i] = GetResourceManager()->GetLiveResource(id).name; else bufs[i] = 0; } } if(m_State <= EXECUTING) { m_Real.glBindBuffersBase(Target, First, Count, bufs); delete[] bufs; } return true; } void WrappedOpenGL::glBindBuffersBase(GLenum target, GLuint first, GLsizei count, const GLuint *buffers) { m_Real.glBindBuffersBase(target, first, count, buffers); if(m_State >= WRITING && first == 0 && count > 0) { size_t idx = BufferIdx(target); if(buffers[0] == 0) m_BufferRecord[idx] = NULL; else m_BufferRecord[idx] = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), buffers[0])); } if(m_State == WRITING_CAPFRAME) { SCOPED_SERIALISE_CONTEXT(BIND_BUFFERS_BASE); Serialise_glBindBuffersBase(target, first, count, buffers); m_ContextRecord->AddChunk(scope.Get()); } } bool WrappedOpenGL::Serialise_glBindBuffersRange(GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes) { SERIALISE_ELEMENT(GLenum, Target, target); SERIALISE_ELEMENT(uint32_t, First, first); SERIALISE_ELEMENT(int32_t, Count, count); GLuint *bufs = NULL; GLintptr *offs = NULL; GLsizeiptr *sz = NULL; if(m_State <= EXECUTING) { bufs = new GLuint[Count]; offs = new GLintptr[Count]; sz = new GLsizeiptr[Count]; } for(int32_t i=0; i < Count; i++) { SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(BufferRes(GetCtx(), buffers[i]))); SERIALISE_ELEMENT(uint64_t, offset, (uint64_t)offsets[i]); SERIALISE_ELEMENT(uint64_t, size, (uint64_t)sizes[i]); if(m_State <= EXECUTING) { if(id != ResourceId()) bufs[i] = GetResourceManager()->GetLiveResource(id).name; else bufs[i] = 0; offs[i] = (GLintptr)offset; sz[i] = (GLsizeiptr)sizes; } } if(m_State <= EXECUTING) { m_Real.glBindBuffersRange(Target, First, Count, bufs, offs, sz); delete[] bufs; delete[] offs; delete[] sz; } return true; } void WrappedOpenGL::glBindBuffersRange(GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes) { m_Real.glBindBuffersRange(target, first, count, buffers, offsets, sizes); if(m_State >= WRITING && first == 0 && count > 0) { size_t idx = BufferIdx(target); if(buffers[0] == 0) m_BufferRecord[idx] = NULL; else m_BufferRecord[idx] = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), buffers[0])); } if(m_State == WRITING_CAPFRAME) { SCOPED_SERIALISE_CONTEXT(BIND_BUFFERS_RANGE); Serialise_glBindBuffersRange(target, first, count, buffers, offsets, sizes); m_ContextRecord->AddChunk(scope.Get()); } } void WrappedOpenGL::glInvalidateBufferData(GLuint buffer) { m_Real.glInvalidateBufferData(buffer); if(m_State == WRITING_IDLE) GetResourceManager()->MarkDirtyResource(BufferRes(GetCtx(), buffer)); } void WrappedOpenGL::glInvalidateBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr length) { m_Real.glInvalidateBufferSubData(buffer, offset, length); if(m_State == WRITING_IDLE) GetResourceManager()->MarkDirtyResource(BufferRes(GetCtx(), buffer)); } #pragma endregion #pragma region Mapping void *WrappedOpenGL::glMapNamedBufferEXT(GLuint buffer, GLenum access) { if(m_State >= WRITING) { GLResourceRecord *record = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), buffer)); GLint length; m_Real.glGetNamedBufferParameterivEXT(buffer, eGL_BUFFER_SIZE, &length); bool straightUp = false; if(m_HighTrafficResources.find(BufferRes(GetCtx(), buffer)) != m_HighTrafficResources.end() && m_State != WRITING_CAPFRAME) straightUp = true; if(GetResourceManager()->IsResourceDirty(record->GetResourceID()) && m_State != WRITING_CAPFRAME) straightUp = true; if(!straightUp && (access == eGL_WRITE_ONLY || access == eGL_READ_WRITE) && m_State != WRITING_CAPFRAME) { straightUp = true; m_HighTrafficResources.insert(BufferRes(GetCtx(), buffer)); if(m_State != WRITING_CAPFRAME) GetResourceManager()->MarkDirtyResource(record->GetResourceID()); } record->Map.offset = 0; record->Map.length = length; record->Map.invalidate = false; if(access == eGL_READ_ONLY) record->Map.access = GL_MAP_READ_BIT; else if(access == eGL_WRITE_ONLY) record->Map.access = GL_MAP_WRITE_BIT; else if(access == eGL_READ_WRITE) record->Map.access = GL_MAP_READ_BIT|GL_MAP_WRITE_BIT; if(straightUp && m_State == WRITING_IDLE) { record->Map.ptr = (byte *)m_Real.glMapNamedBufferEXT(buffer, access); record->Map.status = GLResourceRecord::Mapped_Ignore_Real; return record->Map.ptr; } // TODO align return pointer to GL_MIN_MAP_BUFFER_ALIGNMENT (min 64) if(access == eGL_READ_ONLY) { record->Map.status = GLResourceRecord::Mapped_Read_Real; return m_Real.glMapNamedBufferEXT(buffer, access); } byte *ptr = record->GetDataPtr(); if(ptr == NULL) { RDCWARN("Mapping buffer that hasn't been allocated"); ptr = (byte *)m_Real.glMapNamedBufferEXT(buffer, access); record->Map.ptr = ptr; record->Map.status = GLResourceRecord::Mapped_Write_Real; } else { if(m_State == WRITING_CAPFRAME) { byte *shadow = (byte *)record->GetShadowPtr(0); if(shadow == NULL) { record->AllocShadowStorage(length); shadow = (byte *)record->GetShadowPtr(0); if(GetResourceManager()->IsResourceDirty(record->GetResourceID())) { // TODO get contents from frame initial state m_Real.glGetNamedBufferSubDataEXT(buffer, 0, length, ptr); memcpy(shadow, ptr, length); } else { memcpy(shadow, ptr, length); } memcpy(record->GetShadowPtr(1), shadow, length); } record->Map.ptr = ptr = shadow; record->Map.status = GLResourceRecord::Mapped_Write; } else { record->Map.ptr = ptr; record->Map.status = GLResourceRecord::Mapped_Write; record->UpdateCount++; if(record->UpdateCount > 60) m_HighTrafficResources.insert(BufferRes(GetCtx(), buffer)); } } m_Real.glGetNamedBufferSubDataEXT(buffer, 0, length, ptr); return ptr; } return m_Real.glMapNamedBufferEXT(buffer, access); } void *WrappedOpenGL::glMapBuffer(GLenum target, GLenum access) { if(m_State >= WRITING) { GLResourceRecord *record = m_BufferRecord[BufferIdx(target)]; RDCASSERT(m_BufferRecord[BufferIdx(target)]); if(record) return glMapNamedBufferEXT(GetResourceManager()->GetCurrentResource(record->GetResourceID()).name, access); RDCERR("glMapBuffer: Couldn't get resource record for target %x - no buffer bound?", target); } return m_Real.glMapBuffer(target, access); } void *WrappedOpenGL::glMapNamedBufferRangeEXT(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access) { if(m_State >= WRITING) { GLResourceRecord *record = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), buffer)); bool straightUp = false; if(m_HighTrafficResources.find(BufferRes(GetCtx(), buffer)) != m_HighTrafficResources.end() && m_State != WRITING_CAPFRAME) straightUp = true; if(GetResourceManager()->IsResourceDirty(record->GetResourceID()) && m_State != WRITING_CAPFRAME) straightUp = true; bool invalidateMap = (access & (GL_MAP_INVALIDATE_BUFFER_BIT|GL_MAP_INVALIDATE_RANGE_BIT)) != 0; if(!straightUp && !invalidateMap && (access & GL_MAP_WRITE_BIT) && m_State != WRITING_CAPFRAME) { straightUp = true; m_HighTrafficResources.insert(BufferRes(GetCtx(), buffer)); if(m_State != WRITING_CAPFRAME) GetResourceManager()->MarkDirtyResource(record->GetResourceID()); } record->Map.offset = offset; record->Map.length = length; record->Map.access = access; record->Map.invalidate = invalidateMap; if(straightUp && m_State == WRITING_IDLE) { record->Map.ptr = (byte *)m_Real.glMapNamedBufferRangeEXT(buffer, offset, length, access); record->Map.status = GLResourceRecord::Mapped_Ignore_Real; return record->Map.ptr; } // TODO align return pointer to GL_MIN_MAP_BUFFER_ALIGNMENT (min 64) if((access & (GL_MAP_COHERENT_BIT|GL_MAP_PERSISTENT_BIT|GL_MAP_FLUSH_EXPLICIT_BIT|GL_MAP_UNSYNCHRONIZED_BIT)) != 0) RDCUNIMPLEMENTED("haven't implemented coherent/persistant/flush explicit/unsynchronized glMap calls"); if((access & GL_MAP_READ_BIT) != 0) { byte *ptr = record->GetDataPtr(); if(ptr == NULL) { RDCWARN("Mapping buffer that hasn't been allocated"); record->Map.status = GLResourceRecord::Mapped_Read_Real; return m_Real.glMapNamedBufferRangeEXT(buffer, offset, length, access); } ptr += offset; m_Real.glGetNamedBufferSubDataEXT(buffer, offset, length, ptr); record->Map.status = GLResourceRecord::Mapped_Read; return ptr; } byte *ptr = record->GetDataPtr(); if(ptr == NULL) { RDCWARN("Mapping buffer that hasn't been allocated"); ptr = (byte *)m_Real.glMapNamedBufferRangeEXT(buffer, offset, length, access); record->Map.ptr = ptr; record->Map.status = GLResourceRecord::Mapped_Write_Real; } else { if(m_State == WRITING_CAPFRAME) { byte *shadow = (byte *)record->GetShadowPtr(0); if(shadow == NULL) { GLint buflength; m_Real.glGetNamedBufferParameterivEXT(buffer, eGL_BUFFER_SIZE, &buflength); record->AllocShadowStorage(buflength); shadow = (byte *)record->GetShadowPtr(0); if(!invalidateMap) { if(GetResourceManager()->IsResourceDirty(record->GetResourceID())) { // TODO get contents from frame initial state m_Real.glGetNamedBufferSubDataEXT(buffer, 0, buflength, ptr); memcpy(shadow, ptr, buflength); } else { memcpy(shadow+offset, ptr+offset, length); } } memcpy(record->GetShadowPtr(1), shadow, buflength); } if(invalidateMap) { memset(shadow+offset, 0xcc, length); memset(record->GetShadowPtr(1)+offset, 0xcc, length); } record->Map.ptr = ptr = shadow; record->Map.status = GLResourceRecord::Mapped_Write; } else { ptr += offset; record->Map.ptr = ptr; record->Map.status = GLResourceRecord::Mapped_Write; record->UpdateCount++; if(record->UpdateCount > 60) m_HighTrafficResources.insert(BufferRes(GetCtx(), buffer)); } } return ptr; } return m_Real.glMapNamedBufferRangeEXT(buffer, offset, length, access); } void *WrappedOpenGL::glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) { if(m_State >= WRITING) { GLResourceRecord *record = m_BufferRecord[BufferIdx(target)]; RDCASSERT(m_BufferRecord[BufferIdx(target)]); if(record) return glMapNamedBufferRangeEXT(GetResourceManager()->GetCurrentResource(record->GetResourceID()).name, offset, length, access); RDCERR("glMapBufferRange: Couldn't get resource record for target %x - no buffer bound?", target); } return m_Real.glMapBufferRange(target, offset, length, access); } bool WrappedOpenGL::Serialise_glUnmapNamedBufferEXT(GLuint buffer) { GLResourceRecord *record = NULL; if(m_State >= WRITING) record = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), buffer)); ResourceId bufID; uint64_t offs = 0; uint64_t len = 0; if(m_State < WRITING || m_State == WRITING_CAPFRAME) { SERIALISE_ELEMENT(ResourceId, ID, record->GetResourceID()); SERIALISE_ELEMENT(uint64_t, offset, record->Map.offset); SERIALISE_ELEMENT(uint64_t, length, record->Map.length); bufID = ID; offs = offset; len = length; } else if(m_State == WRITING_IDLE) { bufID = record->GetResourceID(); offs = record->Map.offset; len = record->Map.length; void *ptr = m_Real.glMapNamedBufferRangeEXT(buffer, (GLintptr)offs, (GLsizeiptr)len, GL_MAP_WRITE_BIT); memcpy(ptr, record->Map.ptr, (size_t)len); m_Real.glUnmapNamedBufferEXT(buffer); return true; } uint64_t bufBindStart = 0; size_t diffStart = 0; size_t diffEnd = (size_t)len; if(m_State == WRITING_CAPFRAME && len > 512 && !record->Map.invalidate) { bool found = FindDiffRange(record->Map.ptr, record->GetShadowPtr(1), (size_t)len, diffStart, diffEnd); if(found) { static size_t saved = 0; saved += (size_t)len - (diffEnd-diffStart); RDCDEBUG("Mapped resource size %u, difference: %u -> %u. Total bytes saved so far: %u", (uint32_t)len, (uint32_t)diffStart, (uint32_t)diffEnd, (uint32_t)saved); len = diffEnd-diffStart; } else { diffStart = 0; diffEnd = 0; len = 1; } } if(m_State == WRITING_CAPFRAME && record->GetShadowPtr(1)) { memcpy(record->GetShadowPtr(1)+diffStart, record->Map.ptr+diffStart, diffEnd-diffStart); } SERIALISE_ELEMENT(uint32_t, DiffStart, (uint32_t)diffStart); SERIALISE_ELEMENT(uint32_t, DiffEnd, (uint32_t)diffEnd); SERIALISE_ELEMENT_BUF(byte *, data, record->Map.ptr+diffStart, (size_t)len); if(m_State < WRITING) { GLResource res = GetResourceManager()->GetLiveResource(bufID); buffer = res.name; } if(DiffEnd > DiffStart) { void *ptr = m_Real.glMapNamedBufferRangeEXT(buffer, (GLintptr)(offs+DiffStart), GLsizeiptr(DiffEnd-DiffStart), GL_MAP_WRITE_BIT); memcpy(ptr, data, size_t(DiffEnd-DiffStart)); m_Real.glUnmapNamedBufferEXT(buffer); } if(m_State < WRITING) delete[] data; return true; } GLboolean WrappedOpenGL::glUnmapNamedBufferEXT(GLuint buffer) { if(m_State >= WRITING) { GLResourceRecord *record = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), buffer)); auto status = record->Map.status; GLboolean ret = GL_TRUE; switch(status) { case GLResourceRecord::Unmapped: RDCERR("Unmapped buffer being passed to glUnmapBuffer"); break; case GLResourceRecord::Mapped_Read: // can ignore break; case GLResourceRecord::Mapped_Ignore_Real: if(m_State == WRITING_CAPFRAME) RDCWARN("Failed to cap frame - uncapped Map/Unmap"); // deliberate fallthrough case GLResourceRecord::Mapped_Read_Real: // need to do real unmap ret = m_Real.glUnmapNamedBufferEXT(buffer); break; case GLResourceRecord::Mapped_Write: { if(m_State == WRITING_CAPFRAME) { SCOPED_SERIALISE_CONTEXT(UNMAP); Serialise_glUnmapNamedBufferEXT(buffer); m_ContextRecord->AddChunk(scope.Get()); } else Serialise_glUnmapNamedBufferEXT(buffer); break; } case GLResourceRecord::Mapped_Write_Real: { // Throwing away map contents as we don't have datastore allocated // Could init chunk here using known data (although maybe it's only partial). ret = m_Real.glUnmapNamedBufferEXT(buffer); GetResourceManager()->MarkDirtyResource(record->GetResourceID()); break; } } record->Map.status = GLResourceRecord::Unmapped; return ret; } return m_Real.glUnmapNamedBufferEXT(buffer); } GLboolean WrappedOpenGL::glUnmapBuffer(GLenum target) { if(m_State >= WRITING) { GLResourceRecord *record = m_BufferRecord[BufferIdx(target)]; RDCASSERT(m_BufferRecord[BufferIdx(target)]); if(record) return glUnmapNamedBufferEXT( GetResourceManager()->GetCurrentResource(record->GetResourceID()).name ); RDCERR("glUnmapBuffer: Couldn't get resource record for target %x - no buffer bound?", target); } return m_Real.glUnmapBuffer(target); } void WrappedOpenGL::glFlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length) { m_Real.glFlushMappedBufferRange(target, offset, length); if(m_State >= WRITING) { RDCUNIMPLEMENTED("Persistant/unsynchronised/explicit flash maps are not yet supported"); } } #pragma endregion #pragma region Transform Feedback bool WrappedOpenGL::Serialise_glGenTransformFeedbacks(GLsizei n, GLuint* ids) { SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(FeedbackRes(GetCtx(), *ids))); if(m_State == READING) { GLuint real = 0; m_Real.glGenTransformFeedbacks(1, &real); GLResource res = FeedbackRes(GetCtx(), real); m_ResourceManager->RegisterResource(res); GetResourceManager()->AddLiveResource(id, res); } return true; } void WrappedOpenGL::glGenTransformFeedbacks(GLsizei n, GLuint *ids) { m_Real.glGenTransformFeedbacks(n, ids); for(GLsizei i=0; i < n; i++) { GLResource res = FeedbackRes(GetCtx(), ids[i]); ResourceId id = GetResourceManager()->RegisterResource(res); if(m_State >= WRITING) { Chunk *chunk = NULL; { SCOPED_SERIALISE_CONTEXT(GEN_FEEDBACK); Serialise_glGenTransformFeedbacks(1, ids+i); chunk = scope.Get(); } GLResourceRecord *record = GetResourceManager()->AddResourceRecord(id); RDCASSERT(record); record->AddChunk(chunk); } else { GetResourceManager()->AddLiveResource(id, res); } } } void WrappedOpenGL::glDeleteTransformFeedbacks(GLsizei n, const GLuint *ids) { for(GLsizei i=0; i < n; i++) { GLResource res = FeedbackRes(GetCtx(), ids[i]); if(GetResourceManager()->HasCurrentResource(res)) { if(GetResourceManager()->HasResourceRecord(res)) GetResourceManager()->GetResourceRecord(res)->Delete(GetResourceManager()); GetResourceManager()->UnregisterResource(res); } } m_Real.glDeleteTransformFeedbacks(n, ids); } bool WrappedOpenGL::Serialise_glBindTransformFeedback(GLenum target, GLuint id) { SERIALISE_ELEMENT(GLenum, Target, target); SERIALISE_ELEMENT(ResourceId, fid, GetResourceManager()->GetID(FeedbackRes(GetCtx(), id))); if(m_State <= EXECUTING) { m_Real.glBindTransformFeedback(Target, GetResourceManager()->GetLiveResource(fid).name); } return true; } void WrappedOpenGL::glBindTransformFeedback(GLenum target, GLuint id) { m_Real.glBindTransformFeedback(target, id); if(m_State == WRITING_CAPFRAME) { SCOPED_SERIALISE_CONTEXT(BIND_FEEDBACK); Serialise_glBindTransformFeedback(target, id); m_ContextRecord->AddChunk(scope.Get()); } } bool WrappedOpenGL::Serialise_glBeginTransformFeedback(GLenum primitiveMode) { SERIALISE_ELEMENT(GLenum, Mode, primitiveMode); if(m_State <= EXECUTING) { m_Real.glBeginTransformFeedback(Mode); } return true; } void WrappedOpenGL::glBeginTransformFeedback(GLenum primitiveMode) { m_Real.glBeginTransformFeedback(primitiveMode); if(m_State == WRITING_CAPFRAME) { SCOPED_SERIALISE_CONTEXT(BEGIN_FEEDBACK); Serialise_glBeginTransformFeedback(primitiveMode); m_ContextRecord->AddChunk(scope.Get()); } } bool WrappedOpenGL::Serialise_glPauseTransformFeedback() { if(m_State <= EXECUTING) { m_Real.glPauseTransformFeedback(); } return true; } void WrappedOpenGL::glPauseTransformFeedback() { m_Real.glPauseTransformFeedback(); if(m_State == WRITING_CAPFRAME) { SCOPED_SERIALISE_CONTEXT(PAUSE_FEEDBACK); Serialise_glPauseTransformFeedback(); m_ContextRecord->AddChunk(scope.Get()); } } bool WrappedOpenGL::Serialise_glResumeTransformFeedback() { if(m_State <= EXECUTING) { m_Real.glResumeTransformFeedback(); } return true; } void WrappedOpenGL::glResumeTransformFeedback() { m_Real.glResumeTransformFeedback(); if(m_State == WRITING_CAPFRAME) { SCOPED_SERIALISE_CONTEXT(RESUME_FEEDBACK); Serialise_glResumeTransformFeedback(); m_ContextRecord->AddChunk(scope.Get()); } } bool WrappedOpenGL::Serialise_glEndTransformFeedback() { if(m_State <= EXECUTING) { m_Real.glEndTransformFeedback(); } return true; } void WrappedOpenGL::glEndTransformFeedback() { m_Real.glEndTransformFeedback(); if(m_State == WRITING_CAPFRAME) { SCOPED_SERIALISE_CONTEXT(END_FEEDBACK); Serialise_glEndTransformFeedback(); m_ContextRecord->AddChunk(scope.Get()); } } #pragma endregion #pragma region Vertex Arrays bool WrappedOpenGL::VertexArrayUpdateCheck() { // if no VAO is bound, nothing to check if(m_VertexArrayRecord == NULL) return true; // if we've already stopped tracking this VAO, return as such if(m_VertexArrayRecord && m_VertexArrayRecord->UpdateCount > 64) return false; // increase update count m_VertexArrayRecord->UpdateCount++; // if update count is high, mark as dirty if(m_VertexArrayRecord && m_VertexArrayRecord->UpdateCount > 64) { GetResourceManager()->MarkDirtyResource( m_VertexArrayRecord->GetResourceID() ); return false; } return true; } bool WrappedOpenGL::Serialise_glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) { SERIALISE_ELEMENT(uint32_t, Index, index); SERIALISE_ELEMENT(int32_t, Size, size); SERIALISE_ELEMENT(GLenum, Type, type); SERIALISE_ELEMENT(uint8_t, Norm, normalized); SERIALISE_ELEMENT(uint32_t, Stride, stride); SERIALISE_ELEMENT(uint64_t, Offset, (uint64_t)pointer); SERIALISE_ELEMENT(ResourceId, id, m_VertexArrayRecord ? m_VertexArrayRecord->GetResourceID() : ResourceId()); if(m_State < WRITING) { if(id != ResourceId()) { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glBindVertexArray(res.name); } else { m_Real.glBindVertexArray(0); } m_Real.glVertexAttribPointer(Index, Size, Type, Norm, Stride, (const void *)Offset); } return true; } void WrappedOpenGL::glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) { m_Real.glVertexAttribPointer(index, size, type, normalized, stride, pointer); if(m_State >= WRITING) { GLResourceRecord *r = m_State == WRITING_CAPFRAME ? m_ContextRecord : m_VertexArrayRecord; if(r) { if(m_State == WRITING_IDLE && !VertexArrayUpdateCheck()) return; if(m_State == WRITING_CAPFRAME && m_VertexArrayRecord) GetResourceManager()->MarkResourceFrameReferenced(m_VertexArrayRecord->GetResourceID(), eFrameRef_Write); { SCOPED_SERIALISE_CONTEXT(VERTEXATTRIBPOINTER); Serialise_glVertexAttribPointer(index, size, type, normalized, stride, pointer); r->AddChunk(scope.Get()); } } } } bool WrappedOpenGL::Serialise_glVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer) { SERIALISE_ELEMENT(uint32_t, Index, index); SERIALISE_ELEMENT(int32_t, Size, size); SERIALISE_ELEMENT(GLenum, Type, type); SERIALISE_ELEMENT(uint32_t, Stride, stride); SERIALISE_ELEMENT(uint64_t, Offset, (uint64_t)pointer); SERIALISE_ELEMENT(ResourceId, id, m_VertexArrayRecord ? m_VertexArrayRecord->GetResourceID() : ResourceId()); if(m_State < WRITING) { if(id != ResourceId()) { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glBindVertexArray(res.name); } else { m_Real.glBindVertexArray(0); } m_Real.glVertexAttribIPointer(Index, Size, Type, Stride, (const void *)Offset); } return true; } void WrappedOpenGL::glVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer) { m_Real.glVertexAttribIPointer(index, size, type, stride, pointer); if(m_State >= WRITING) { GLResourceRecord *r = m_State == WRITING_CAPFRAME ? m_ContextRecord : m_VertexArrayRecord; if(r) { if(m_State == WRITING_IDLE && !VertexArrayUpdateCheck()) return; if(m_State == WRITING_CAPFRAME && m_VertexArrayRecord) GetResourceManager()->MarkResourceFrameReferenced(m_VertexArrayRecord->GetResourceID(), eFrameRef_Write); { SCOPED_SERIALISE_CONTEXT(VERTEXATTRIBIPOINTER); Serialise_glVertexAttribIPointer(index, size, type, stride, pointer); r->AddChunk(scope.Get()); } } } } bool WrappedOpenGL::Serialise_glVertexAttribLPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer) { SERIALISE_ELEMENT(uint32_t, Index, index); SERIALISE_ELEMENT(int32_t, Size, size); SERIALISE_ELEMENT(GLenum, Type, type); SERIALISE_ELEMENT(uint32_t, Stride, stride); SERIALISE_ELEMENT(uint64_t, Offset, (uint64_t)pointer); SERIALISE_ELEMENT(ResourceId, id, m_VertexArrayRecord ? m_VertexArrayRecord->GetResourceID() : ResourceId()); if(m_State < WRITING) { if(id != ResourceId()) { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glBindVertexArray(res.name); } else { m_Real.glBindVertexArray(0); } m_Real.glVertexAttribLPointer(Index, Size, Type, Stride, (const void *)Offset); } return true; } void WrappedOpenGL::glVertexAttribLPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer) { m_Real.glVertexAttribLPointer(index, size, type, stride, pointer); if(m_State >= WRITING) { GLResourceRecord *r = m_State == WRITING_CAPFRAME ? m_ContextRecord : m_VertexArrayRecord; if(r) { if(m_State == WRITING_IDLE && !VertexArrayUpdateCheck()) return; if(m_State == WRITING_CAPFRAME && m_VertexArrayRecord) GetResourceManager()->MarkResourceFrameReferenced(m_VertexArrayRecord->GetResourceID(), eFrameRef_Write); { SCOPED_SERIALISE_CONTEXT(VERTEXATTRIBLPOINTER); Serialise_glVertexAttribLPointer(index, size, type, stride, pointer); r->AddChunk(scope.Get()); } } } } bool WrappedOpenGL::Serialise_glVertexAttribBinding(GLuint attribindex, GLuint bindingindex) { SERIALISE_ELEMENT(uint32_t, aidx, attribindex); SERIALISE_ELEMENT(uint32_t, bidx, bindingindex); SERIALISE_ELEMENT(ResourceId, id, m_VertexArrayRecord ? m_VertexArrayRecord->GetResourceID() : ResourceId()); if(m_State < WRITING) { if(id != ResourceId()) { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glBindVertexArray(res.name); } else { m_Real.glBindVertexArray(0); } m_Real.glVertexAttribBinding(aidx, bidx); } return true; } void WrappedOpenGL::glVertexAttribBinding(GLuint attribindex, GLuint bindingindex) { m_Real.glVertexAttribBinding(attribindex, bindingindex); if(m_State >= WRITING) { GLResourceRecord *r = m_State == WRITING_CAPFRAME ? m_ContextRecord : m_VertexArrayRecord; if(r) { if(m_State == WRITING_IDLE && !VertexArrayUpdateCheck()) return; if(m_State == WRITING_CAPFRAME && m_VertexArrayRecord) GetResourceManager()->MarkResourceFrameReferenced(m_VertexArrayRecord->GetResourceID(), eFrameRef_Write); { SCOPED_SERIALISE_CONTEXT(VERTEXATTRIBBINDING); Serialise_glVertexAttribBinding(attribindex, bindingindex); r->AddChunk(scope.Get()); } } } } bool WrappedOpenGL::Serialise_glVertexAttribFormat(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) { SERIALISE_ELEMENT(uint32_t, Index, attribindex); SERIALISE_ELEMENT(int32_t, Size, size); SERIALISE_ELEMENT(bool, Norm, normalized ? true : false); SERIALISE_ELEMENT(GLenum, Type, type); SERIALISE_ELEMENT(uint32_t, Offset, relativeoffset); SERIALISE_ELEMENT(ResourceId, id, m_VertexArrayRecord ? m_VertexArrayRecord->GetResourceID() : ResourceId()); if(m_State < WRITING) { if(id != ResourceId()) { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glBindVertexArray(res.name); } else { m_Real.glBindVertexArray(0); } m_Real.glVertexAttribFormat(Index, Size, Type, Norm, Offset); } return true; } void WrappedOpenGL::glVertexAttribFormat(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) { m_Real.glVertexAttribFormat(attribindex, size, type, normalized, relativeoffset); if(m_State >= WRITING) { GLResourceRecord *r = m_State == WRITING_CAPFRAME ? m_ContextRecord : m_VertexArrayRecord; if(r) { if(m_State == WRITING_IDLE && !VertexArrayUpdateCheck()) return; if(m_State == WRITING_CAPFRAME && m_VertexArrayRecord) GetResourceManager()->MarkResourceFrameReferenced(m_VertexArrayRecord->GetResourceID(), eFrameRef_Write); { SCOPED_SERIALISE_CONTEXT(VERTEXATTRIBFORMAT); Serialise_glVertexAttribFormat(attribindex, size, type, normalized, relativeoffset); r->AddChunk(scope.Get()); } } } } bool WrappedOpenGL::Serialise_glVertexAttribIFormat(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) { SERIALISE_ELEMENT(uint32_t, Index, attribindex); SERIALISE_ELEMENT(int32_t, Size, size); SERIALISE_ELEMENT(GLenum, Type, type); SERIALISE_ELEMENT(uint32_t, Offset, relativeoffset); SERIALISE_ELEMENT(ResourceId, id, m_VertexArrayRecord ? m_VertexArrayRecord->GetResourceID() : ResourceId()); if(m_State < WRITING) { if(id != ResourceId()) { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glBindVertexArray(res.name); } else { m_Real.glBindVertexArray(0); } m_Real.glVertexAttribIFormat(Index, Size, Type, Offset); } return true; } void WrappedOpenGL::glVertexAttribIFormat(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) { m_Real.glVertexAttribIFormat(attribindex, size, type, relativeoffset); if(m_State >= WRITING) { GLResourceRecord *r = m_State == WRITING_CAPFRAME ? m_ContextRecord : m_VertexArrayRecord; if(r) { if(m_State == WRITING_IDLE && !VertexArrayUpdateCheck()) return; if(m_State == WRITING_CAPFRAME && m_VertexArrayRecord) GetResourceManager()->MarkResourceFrameReferenced(m_VertexArrayRecord->GetResourceID(), eFrameRef_Write); { SCOPED_SERIALISE_CONTEXT(VERTEXATTRIBIFORMAT); Serialise_glVertexAttribIFormat(attribindex, size, type, relativeoffset); r->AddChunk(scope.Get()); } } } } bool WrappedOpenGL::Serialise_glVertexAttribLFormat(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) { SERIALISE_ELEMENT(uint32_t, Index, attribindex); SERIALISE_ELEMENT(int32_t, Size, size); SERIALISE_ELEMENT(GLenum, Type, type); SERIALISE_ELEMENT(uint32_t, Offset, relativeoffset); SERIALISE_ELEMENT(ResourceId, id, m_VertexArrayRecord ? m_VertexArrayRecord->GetResourceID() : ResourceId()); if(m_State < WRITING) { if(id != ResourceId()) { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glBindVertexArray(res.name); } else { m_Real.glBindVertexArray(0); } m_Real.glVertexAttribLFormat(Index, Size, Type, Offset); } return true; } void WrappedOpenGL::glVertexAttribLFormat(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) { m_Real.glVertexAttribLFormat(attribindex, size, type, relativeoffset); if(m_State >= WRITING) { GLResourceRecord *r = m_State == WRITING_CAPFRAME ? m_ContextRecord : m_VertexArrayRecord; if(r) { if(m_State == WRITING_IDLE && !VertexArrayUpdateCheck()) return; if(m_State == WRITING_CAPFRAME && m_VertexArrayRecord) GetResourceManager()->MarkResourceFrameReferenced(m_VertexArrayRecord->GetResourceID(), eFrameRef_Write); { SCOPED_SERIALISE_CONTEXT(VERTEXATTRIBLFORMAT); Serialise_glVertexAttribLFormat(attribindex, size, type, relativeoffset); r->AddChunk(scope.Get()); } } } } bool WrappedOpenGL::Serialise_glVertexAttribDivisor(GLuint index, GLuint divisor) { SERIALISE_ELEMENT(uint32_t, Index, index); SERIALISE_ELEMENT(uint32_t, Divisor, divisor); SERIALISE_ELEMENT(ResourceId, id, m_VertexArrayRecord ? m_VertexArrayRecord->GetResourceID() : ResourceId()); if(m_State < WRITING) { if(id != ResourceId()) { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glBindVertexArray(res.name); } else { m_Real.glBindVertexArray(0); } m_Real.glVertexAttribDivisor(Index, Divisor); } return true; } void WrappedOpenGL::glVertexAttribDivisor(GLuint index, GLuint divisor) { m_Real.glVertexAttribDivisor(index, divisor); if(m_State >= WRITING) { GLResourceRecord *r = m_State == WRITING_CAPFRAME ? m_ContextRecord : m_VertexArrayRecord; if(r) { if(m_State == WRITING_IDLE && !VertexArrayUpdateCheck()) return; if(m_State == WRITING_CAPFRAME && m_VertexArrayRecord) GetResourceManager()->MarkResourceFrameReferenced(m_VertexArrayRecord->GetResourceID(), eFrameRef_Write); { SCOPED_SERIALISE_CONTEXT(VERTEXATTRIBDIVISOR); Serialise_glVertexAttribDivisor(index, divisor); r->AddChunk(scope.Get()); } } } } bool WrappedOpenGL::Serialise_glEnableVertexAttribArray(GLuint index) { SERIALISE_ELEMENT(uint32_t, Index, index); SERIALISE_ELEMENT(ResourceId, id, m_VertexArrayRecord ? m_VertexArrayRecord->GetResourceID() : ResourceId()); if(m_State < WRITING) { if(id != ResourceId()) { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glBindVertexArray(res.name); } else { m_Real.glBindVertexArray(0); } m_Real.glEnableVertexAttribArray(Index); } return true; } void WrappedOpenGL::glEnableVertexAttribArray(GLuint index) { m_Real.glEnableVertexAttribArray(index); if(m_State >= WRITING) { GLResourceRecord *r = m_State == WRITING_CAPFRAME ? m_ContextRecord : m_VertexArrayRecord; if(r) { if(m_State == WRITING_IDLE && !VertexArrayUpdateCheck()) return; if(m_State == WRITING_CAPFRAME && m_VertexArrayRecord) GetResourceManager()->MarkResourceFrameReferenced(m_VertexArrayRecord->GetResourceID(), eFrameRef_Write); { SCOPED_SERIALISE_CONTEXT(ENABLEVERTEXATTRIBARRAY); Serialise_glEnableVertexAttribArray(index); r->AddChunk(scope.Get()); } } } } bool WrappedOpenGL::Serialise_glDisableVertexAttribArray(GLuint index) { SERIALISE_ELEMENT(uint32_t, Index, index); SERIALISE_ELEMENT(ResourceId, id, m_VertexArrayRecord ? m_VertexArrayRecord->GetResourceID() : ResourceId()); if(m_State < WRITING) { if(id != ResourceId()) { GLResource res = GetResourceManager()->GetLiveResource(id); m_Real.glBindVertexArray(res.name); } else { m_Real.glBindVertexArray(0); } m_Real.glDisableVertexAttribArray(Index); } return true; } void WrappedOpenGL::glDisableVertexAttribArray(GLuint index) { m_Real.glDisableVertexAttribArray(index); if(m_State >= WRITING) { GLResourceRecord *r = m_State == WRITING_CAPFRAME ? m_ContextRecord : m_VertexArrayRecord; if(r) { if(m_State == WRITING_IDLE && !VertexArrayUpdateCheck()) return; if(m_State == WRITING_CAPFRAME && m_VertexArrayRecord) GetResourceManager()->MarkResourceFrameReferenced(m_VertexArrayRecord->GetResourceID(), eFrameRef_Write); { SCOPED_SERIALISE_CONTEXT(DISABLEVERTEXATTRIBARRAY); Serialise_glDisableVertexAttribArray(index); r->AddChunk(scope.Get()); } } } } bool WrappedOpenGL::Serialise_glGenVertexArrays(GLsizei n, GLuint* arrays) { SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(VertexArrayRes(GetCtx(), *arrays))); if(m_State == READING) { GLuint real = 0; m_Real.glGenVertexArrays(1, &real); GLResource res = VertexArrayRes(GetCtx(), real); m_ResourceManager->RegisterResource(res); GetResourceManager()->AddLiveResource(id, res); } return true; } void WrappedOpenGL::glGenVertexArrays(GLsizei n, GLuint *arrays) { m_Real.glGenVertexArrays(n, arrays); for(GLsizei i=0; i < n; i++) { GLResource res = VertexArrayRes(GetCtx(), arrays[i]); ResourceId id = GetResourceManager()->RegisterResource(res); if(m_State >= WRITING) { Chunk *chunk = NULL; { SCOPED_SERIALISE_CONTEXT(GEN_VERTEXARRAY); Serialise_glGenVertexArrays(1, arrays+i); chunk = scope.Get(); } GLResourceRecord *record = GetResourceManager()->AddResourceRecord(id); RDCASSERT(record); record->AddChunk(chunk); } else { GetResourceManager()->AddLiveResource(id, res); } } } bool WrappedOpenGL::Serialise_glBindVertexArray(GLuint array) { SERIALISE_ELEMENT(ResourceId, id, (array ? GetResourceManager()->GetID(VertexArrayRes(GetCtx(), array)) : ResourceId())); if(m_State <= EXECUTING) { if(id == ResourceId()) { m_Real.glBindVertexArray(0); } else { GLuint live = GetResourceManager()->GetLiveResource(id).name; m_Real.glBindVertexArray(live); } } return true; } void WrappedOpenGL::glBindVertexArray(GLuint array) { m_Real.glBindVertexArray(array); if(m_State >= WRITING) { if(array == 0) { m_VertexArrayRecord = NULL; } else { m_VertexArrayRecord = GetResourceManager()->GetResourceRecord(VertexArrayRes(GetCtx(), array)); } } if(m_State == WRITING_CAPFRAME) { SCOPED_SERIALISE_CONTEXT(BIND_VERTEXARRAY); Serialise_glBindVertexArray(array); m_ContextRecord->AddChunk(scope.Get()); } } bool WrappedOpenGL::Serialise_glBindVertexBuffer(GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) { SERIALISE_ELEMENT(uint32_t, idx, bindingindex); SERIALISE_ELEMENT(ResourceId, id, (buffer ? GetResourceManager()->GetID(BufferRes(GetCtx(), buffer)) : ResourceId())); SERIALISE_ELEMENT(uint64_t, offs, offset); SERIALISE_ELEMENT(uint64_t, str, stride); if(m_State <= EXECUTING) { GLuint live = 0; if(id != ResourceId()) live = GetResourceManager()->GetLiveResource(id).name; m_Real.glBindVertexBuffer(idx, live, (GLintptr)offs, (GLsizei)str); } return true; } void WrappedOpenGL::glBindVertexBuffer(GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) { m_Real.glBindVertexBuffer(bindingindex, buffer, offset, stride); if(m_State == WRITING_CAPFRAME) { SCOPED_SERIALISE_CONTEXT(BIND_VERTEXBUFFER); Serialise_glBindVertexBuffer(bindingindex, buffer, offset, stride); m_ContextRecord->AddChunk(scope.Get()); } } bool WrappedOpenGL::Serialise_glBindVertexBuffers(GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides) { SERIALISE_ELEMENT(uint32_t, First, first); SERIALISE_ELEMENT(int32_t, Count, count); GLuint *bufs = NULL; GLintptr *offs = NULL; GLsizei *str = NULL; if(m_State <= EXECUTING) { bufs = new GLuint[Count]; offs = new GLintptr[Count]; str = new GLsizei[Count]; } for(int32_t i=0; i < Count; i++) { SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(BufferRes(GetCtx(), buffers[i]))); SERIALISE_ELEMENT(uint64_t, offset, (uint64_t)offsets[i]); SERIALISE_ELEMENT(uint64_t, stride, (uint64_t)strides[i]); if(m_State <= EXECUTING) { if(id != ResourceId()) bufs[i] = GetResourceManager()->GetLiveResource(id).name; else bufs[i] = 0; offs[i] = (GLintptr)offset; str[i] = (GLsizei)stride; } } if(m_State <= EXECUTING) { m_Real.glBindVertexBuffers(First, Count, bufs, offs, str); delete[] bufs; delete[] offs; delete[] str; } return true; } void WrappedOpenGL::glBindVertexBuffers(GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides) { m_Real.glBindVertexBuffers(first, count, buffers, offsets, strides); if(m_State == WRITING_CAPFRAME) { SCOPED_SERIALISE_CONTEXT(BIND_VERTEXBUFFERS); Serialise_glBindVertexBuffers(first, count, buffers, offsets, strides); m_ContextRecord->AddChunk(scope.Get()); } } bool WrappedOpenGL::Serialise_glVertexBindingDivisor(GLuint bindingindex, GLuint divisor) { SERIALISE_ELEMENT(uint32_t, idx, bindingindex); SERIALISE_ELEMENT(uint32_t, d, divisor); if(m_State <= EXECUTING) m_Real.glVertexBindingDivisor(idx, d); return true; } void WrappedOpenGL::glVertexBindingDivisor(GLuint bindingindex, GLuint divisor) { m_Real.glVertexBindingDivisor(bindingindex, divisor); if(m_State == WRITING_CAPFRAME) { SCOPED_SERIALISE_CONTEXT(VERTEXBINDINGDIVISOR); Serialise_glVertexBindingDivisor(bindingindex, divisor); m_ContextRecord->AddChunk(scope.Get()); } } void WrappedOpenGL::glDeleteBuffers(GLsizei n, const GLuint *buffers) { for(GLsizei i=0; i < n; i++) { GLResource res = BufferRes(GetCtx(), buffers[i]); if(GetResourceManager()->HasCurrentResource(res)) { if(GetResourceManager()->HasResourceRecord(res)) GetResourceManager()->GetResourceRecord(res)->Delete(GetResourceManager()); GetResourceManager()->UnregisterResource(res); } } m_Real.glDeleteBuffers(n, buffers); } void WrappedOpenGL::glDeleteVertexArrays(GLsizei n, const GLuint *arrays) { for(GLsizei i=0; i < n; i++) { GLResource res = VertexArrayRes(GetCtx(), arrays[i]); if(GetResourceManager()->HasCurrentResource(res)) { if(GetResourceManager()->HasResourceRecord(res)) GetResourceManager()->GetResourceRecord(res)->Delete(GetResourceManager()); GetResourceManager()->UnregisterResource(res); } } m_Real.glDeleteVertexArrays(n, arrays); } #pragma endregion #pragma region Horrible glVertexAttrib variants bool WrappedOpenGL::Serialise_glVertexAttrib(GLuint index, int count, GLenum type, GLboolean normalized, const void *value, int attribtype) { SERIALISE_ELEMENT(uint32_t, idx, index); SERIALISE_ELEMENT(int32_t, Count, count); SERIALISE_ELEMENT(int, Type, attribtype); SERIALISE_ELEMENT(bool, norm, normalized == GL_TRUE); SERIALISE_ELEMENT(GLenum, packedType, type); AttribType attr = AttribType(Type & Attrib_typemask); size_t elemSize = 1; switch(attr) { case Attrib_GLdouble: elemSize = 8; break; case Attrib_GLfloat: case Attrib_GLint: case Attrib_GLuint: case Attrib_packed: elemSize = 4; break; case Attrib_GLshort: case Attrib_GLushort: elemSize = 2; break; default: case Attrib_GLbyte: case Attrib_GLubyte: elemSize = 1; break; } size_t valueSize = elemSize*Count; if(Type == Attrib_packed) valueSize = sizeof(uint32_t); if(m_State >= WRITING) { m_pSerialiser->RawWriteBytes(value, valueSize); } else if(m_State <= EXECUTING) { value = m_pSerialiser->RawReadBytes(valueSize); if(Type == Attrib_packed) { if(Count == 1) m_Real.glVertexAttribP1uiv(idx, packedType, norm, (GLuint*)value); else if(Count == 2) m_Real.glVertexAttribP2uiv(idx, packedType, norm, (GLuint*)value); else if(Count == 3) m_Real.glVertexAttribP3uiv(idx, packedType, norm, (GLuint*)value); else if(Count == 4) m_Real.glVertexAttribP4uiv(idx, packedType, norm, (GLuint*)value); } else if(Type & Attrib_I) { if(Count == 1) { if(attr == Attrib_GLint) m_Real.glVertexAttribI1iv(idx, (GLint*)value); else if(attr == Attrib_GLuint) m_Real.glVertexAttribI1uiv(idx, (GLuint*)value); } else if(Count == 2) { if(attr == Attrib_GLint) m_Real.glVertexAttribI2iv(idx, (GLint*)value); else if(attr == Attrib_GLuint) m_Real.glVertexAttribI2uiv(idx, (GLuint*)value); } else if(Count == 2) { if(attr == Attrib_GLint) m_Real.glVertexAttribI3iv(idx, (GLint*)value); else if(attr == Attrib_GLuint) m_Real.glVertexAttribI3uiv(idx, (GLuint*)value); } else { if(attr == Attrib_GLbyte) m_Real.glVertexAttribI4bv(idx, (GLbyte*)value); else if(attr == Attrib_GLint) m_Real.glVertexAttribI4iv(idx, (GLint*)value); else if(attr == Attrib_GLshort) m_Real.glVertexAttribI4sv(idx, (GLshort*)value); else if(attr == Attrib_GLubyte) m_Real.glVertexAttribI4ubv(idx, (GLubyte*)value); else if(attr == Attrib_GLuint) m_Real.glVertexAttribI4uiv(idx, (GLuint*)value); else if(attr == Attrib_GLushort) m_Real.glVertexAttribI4usv(idx, (GLushort*)value); } } else if(Type & Attrib_L) { if(Count == 1) m_Real.glVertexAttribL1dv(idx, (GLdouble*)value); else if(Count == 2) m_Real.glVertexAttribL2dv(idx, (GLdouble*)value); else if(Count == 3) m_Real.glVertexAttribL3dv(idx, (GLdouble*)value); else if(Count == 4) m_Real.glVertexAttribL4dv(idx, (GLdouble*)value); } else if(Type & Attrib_N) { if(attr == Attrib_GLbyte) m_Real.glVertexAttrib4Nbv(idx, (GLbyte*)value); else if(attr == Attrib_GLint) m_Real.glVertexAttrib4Niv(idx, (GLint*)value); else if(attr == Attrib_GLshort) m_Real.glVertexAttrib4Nsv(idx, (GLshort*)value); else if(attr == Attrib_GLubyte) m_Real.glVertexAttrib4Nubv(idx, (GLubyte*)value); else if(attr == Attrib_GLuint) m_Real.glVertexAttrib4Nuiv(idx, (GLuint*)value); else if(attr == Attrib_GLushort) m_Real.glVertexAttrib4Nusv(idx, (GLushort*)value); } else { if(Count == 1) { if(attr == Attrib_GLdouble) m_Real.glVertexAttrib1dv(idx, (GLdouble*)value); else if(attr == Attrib_GLfloat) m_Real.glVertexAttrib1fv(idx, (GLfloat*)value); else if(attr == Attrib_GLshort) m_Real.glVertexAttrib1sv(idx, (GLshort*)value); } else if(Count == 2) { if(attr == Attrib_GLdouble) m_Real.glVertexAttrib2dv(idx, (GLdouble*)value); else if(attr == Attrib_GLfloat) m_Real.glVertexAttrib2fv(idx, (GLfloat*)value); else if(attr == Attrib_GLshort) m_Real.glVertexAttrib2sv(idx, (GLshort*)value); } else if(Count == 3) { if(attr == Attrib_GLdouble) m_Real.glVertexAttrib3dv(idx, (GLdouble*)value); else if(attr == Attrib_GLfloat) m_Real.glVertexAttrib3fv(idx, (GLfloat*)value); else if(attr == Attrib_GLshort) m_Real.glVertexAttrib3sv(idx, (GLshort*)value); } else { if(attr == Attrib_GLdouble) m_Real.glVertexAttrib4dv(idx, (GLdouble*)value); else if(attr == Attrib_GLfloat) m_Real.glVertexAttrib4fv(idx, (GLfloat*)value); else if(attr == Attrib_GLbyte) m_Real.glVertexAttrib4bv(idx, (GLbyte*)value); else if(attr == Attrib_GLint) m_Real.glVertexAttrib4iv(idx, (GLint*)value); else if(attr == Attrib_GLshort) m_Real.glVertexAttrib4sv(idx, (GLshort*)value); else if(attr == Attrib_GLubyte) m_Real.glVertexAttrib4ubv(idx, (GLubyte*)value); else if(attr == Attrib_GLuint) m_Real.glVertexAttrib4uiv(idx, (GLuint*)value); else if(attr == Attrib_GLushort) m_Real.glVertexAttrib4usv(idx, (GLushort*)value); } } } return true; } #define ATTRIB_FUNC(count, suffix, TypeOr, paramtype, ...) \ void WrappedOpenGL::CONCAT(glVertexAttrib, suffix)(GLuint index, __VA_ARGS__) \ { \ m_Real.CONCAT(glVertexAttrib, suffix)(index, ARRAYLIST); \ \ if(m_State >= WRITING_CAPFRAME) \ { \ SCOPED_SERIALISE_CONTEXT(VERTEXATTRIB_GENERIC); \ const paramtype vals[] = { ARRAYLIST }; \ Serialise_glVertexAttrib(index, count, eGL_NONE, GL_FALSE, vals, TypeOr | CONCAT(Attrib_, paramtype)); \ \ m_ContextRecord->AddChunk(scope.Get()); \ } \ } #define ARRAYLIST x ATTRIB_FUNC(1, 1f, 0, GLfloat, GLfloat x) ATTRIB_FUNC(1, 1s, 0, GLshort, GLshort x) ATTRIB_FUNC(1, 1d, 0, GLdouble, GLdouble x) ATTRIB_FUNC(1, L1d, Attrib_L, GLdouble, GLdouble x) ATTRIB_FUNC(1, I1i, Attrib_I, GLint, GLint x) ATTRIB_FUNC(1, I1ui, Attrib_I, GLuint, GLuint x) #undef ARRAYLIST #define ARRAYLIST x, y ATTRIB_FUNC(2, 2f, 0, GLfloat, GLfloat x, GLfloat y) ATTRIB_FUNC(2, 2s, 0, GLshort, GLshort x, GLshort y) ATTRIB_FUNC(2, 2d, 0, GLdouble, GLdouble x, GLdouble y) ATTRIB_FUNC(2, L2d, Attrib_L, GLdouble, GLdouble x, GLdouble y) ATTRIB_FUNC(2, I2i, Attrib_I, GLint, GLint x, GLint y) ATTRIB_FUNC(2, I2ui, Attrib_I, GLuint, GLuint x, GLuint y) #undef ARRAYLIST #define ARRAYLIST x, y, z ATTRIB_FUNC(3, 3f, 0, GLfloat, GLfloat x, GLfloat y, GLfloat z) ATTRIB_FUNC(3, 3s, 0, GLshort, GLshort x, GLshort y, GLshort z) ATTRIB_FUNC(3, 3d, 0, GLdouble, GLdouble x, GLdouble y, GLdouble z) ATTRIB_FUNC(3, L3d, Attrib_L, GLdouble, GLdouble x, GLdouble y, GLdouble z) ATTRIB_FUNC(3, I3i, Attrib_I, GLint, GLint x, GLint y, GLint z) ATTRIB_FUNC(3, I3ui, Attrib_I, GLuint, GLuint x, GLuint y, GLuint z) #undef ARRAYLIST #define ARRAYLIST x, y, z, w ATTRIB_FUNC(4, 4f, 0, GLfloat, GLfloat x, GLfloat y, GLfloat z, GLfloat w) ATTRIB_FUNC(4, 4s, 0, GLshort, GLshort x, GLshort y, GLshort z, GLshort w) ATTRIB_FUNC(4, 4d, 0, GLdouble, GLdouble x, GLdouble y, GLdouble z, GLdouble w) ATTRIB_FUNC(4, L4d, Attrib_L, GLdouble, GLdouble x, GLdouble y, GLdouble z, GLdouble w) ATTRIB_FUNC(4, I4i, Attrib_I, GLint, GLint x, GLint y, GLint z, GLint w) ATTRIB_FUNC(4, I4ui, Attrib_I, GLuint, GLuint x, GLuint y, GLuint z, GLuint w) ATTRIB_FUNC(4, 4Nub, Attrib_N, GLubyte, GLubyte x, GLubyte y, GLubyte z, GLubyte w) #undef ATTRIB_FUNC #define ATTRIB_FUNC(count, suffix, TypeOr, paramtype) \ void WrappedOpenGL::CONCAT(glVertexAttrib, suffix)(GLuint index, const paramtype *value) \ { \ m_Real.CONCAT(glVertexAttrib, suffix)(index, value); \ \ if(m_State >= WRITING_CAPFRAME) \ { \ SCOPED_SERIALISE_CONTEXT(VERTEXATTRIB_GENERIC); \ Serialise_glVertexAttrib(index, count, eGL_NONE, GL_FALSE, value, TypeOr | CONCAT(Attrib_, paramtype)); \ \ m_ContextRecord->AddChunk(scope.Get()); \ } \ } ATTRIB_FUNC(1, 1dv, 0, GLdouble) ATTRIB_FUNC(2, 2dv, 0, GLdouble) ATTRIB_FUNC(3, 3dv, 0, GLdouble) ATTRIB_FUNC(4, 4dv, 0, GLdouble) ATTRIB_FUNC(1, 1sv, 0, GLshort) ATTRIB_FUNC(2, 2sv, 0, GLshort) ATTRIB_FUNC(3, 3sv, 0, GLshort) ATTRIB_FUNC(4, 4sv, 0, GLshort) ATTRIB_FUNC(1, 1fv, 0, GLfloat) ATTRIB_FUNC(2, 2fv, 0, GLfloat) ATTRIB_FUNC(3, 3fv, 0, GLfloat) ATTRIB_FUNC(4, 4fv, 0, GLfloat) ATTRIB_FUNC(4, 4bv, 0, GLbyte) ATTRIB_FUNC(4, 4iv, 0, GLint) ATTRIB_FUNC(4, 4uiv, 0, GLuint) ATTRIB_FUNC(4, 4usv, 0, GLushort) ATTRIB_FUNC(4, 4ubv, 0, GLubyte) ATTRIB_FUNC(1, L1dv, Attrib_L, GLdouble) ATTRIB_FUNC(2, L2dv, Attrib_L, GLdouble) ATTRIB_FUNC(3, L3dv, Attrib_L, GLdouble) ATTRIB_FUNC(4, L4dv, Attrib_L, GLdouble) ATTRIB_FUNC(1, I1iv, Attrib_I, GLint) ATTRIB_FUNC(1, I1uiv, Attrib_I, GLuint) ATTRIB_FUNC(2, I2iv, Attrib_I, GLint) ATTRIB_FUNC(2, I2uiv, Attrib_I, GLuint) ATTRIB_FUNC(3, I3iv, Attrib_I, GLint) ATTRIB_FUNC(3, I3uiv, Attrib_I, GLuint) ATTRIB_FUNC(4, I4bv, Attrib_I, GLbyte) ATTRIB_FUNC(4, I4iv, Attrib_I, GLint) ATTRIB_FUNC(4, I4sv, Attrib_I, GLshort) ATTRIB_FUNC(4, I4ubv, Attrib_I, GLubyte) ATTRIB_FUNC(4, I4uiv, Attrib_I, GLuint) ATTRIB_FUNC(4, I4usv, Attrib_I, GLushort) ATTRIB_FUNC(4, 4Nbv, Attrib_N, GLbyte) ATTRIB_FUNC(4, 4Niv, Attrib_N, GLint) ATTRIB_FUNC(4, 4Nsv, Attrib_N, GLshort) ATTRIB_FUNC(4, 4Nubv, Attrib_N, GLubyte) ATTRIB_FUNC(4, 4Nuiv, Attrib_N, GLuint) ATTRIB_FUNC(4, 4Nusv, Attrib_N, GLushort) #undef ATTRIB_FUNC #define ATTRIB_FUNC(count, suffix, funcparam, passparam) \ void WrappedOpenGL::CONCAT(CONCAT(glVertexAttribP, count), suffix)(GLuint index, GLenum type, GLboolean normalized, funcparam) \ { \ m_Real.CONCAT(CONCAT(glVertexAttribP, count), suffix)(index, type, normalized, value); \ \ if(m_State >= WRITING_CAPFRAME) \ { \ SCOPED_SERIALISE_CONTEXT(VERTEXATTRIB_GENERIC); \ Serialise_glVertexAttrib(index, count, type, normalized, passparam, Attrib_packed); \ \ m_ContextRecord->AddChunk(scope.Get()); \ } \ } ATTRIB_FUNC(1, ui, GLuint value, &value) ATTRIB_FUNC(2, ui, GLuint value, &value) ATTRIB_FUNC(3, ui, GLuint value, &value) ATTRIB_FUNC(4, ui, GLuint value, &value) ATTRIB_FUNC(1, uiv, const GLuint *value, value) ATTRIB_FUNC(2, uiv, const GLuint *value, value) ATTRIB_FUNC(3, uiv, const GLuint *value, value) ATTRIB_FUNC(4, uiv, const GLuint *value, value) #pragma endregion