diff options
Diffstat (limited to 'Source/Plugins/Plugin_VideoOGL/Src/StreamBuffer.cpp')
| -rw-r--r-- | Source/Plugins/Plugin_VideoOGL/Src/StreamBuffer.cpp | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/Source/Plugins/Plugin_VideoOGL/Src/StreamBuffer.cpp b/Source/Plugins/Plugin_VideoOGL/Src/StreamBuffer.cpp new file mode 100644 index 0000000000..24b6b7d9b4 --- /dev/null +++ b/Source/Plugins/Plugin_VideoOGL/Src/StreamBuffer.cpp @@ -0,0 +1,242 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Globals.h" +#include "GLUtil.h" +#include "StreamBuffer.h" +#include "MemoryUtil.h" +#include "Render.h" + +namespace OGL +{ + +static const u32 SYNC_POINTS = 16; +static const u32 ALIGN_PINNED_MEMORY = 4096; + +StreamBuffer::StreamBuffer(u32 type, size_t size, StreamType uploadType) +: m_uploadtype(uploadType), m_buffertype(type), m_size(size) +{ + glGenBuffers(1, &m_buffer); + + bool nvidia = !strcmp(g_ogl_config.gl_vendor, "NVIDIA Corporation"); + + if(m_uploadtype & STREAM_DETECT) + { + if(!g_ogl_config.bSupportsGLBaseVertex && (m_uploadtype & BUFFERSUBDATA)) + m_uploadtype = BUFFERSUBDATA; + else if(g_ogl_config.bSupportsGLSync && g_Config.bHackedBufferUpload && (m_uploadtype & MAP_AND_RISK)) + m_uploadtype = MAP_AND_RISK; + else if(g_ogl_config.bSupportsGLSync && g_ogl_config.bSupportsGLPinnedMemory && (m_uploadtype & PINNED_MEMORY)) + m_uploadtype = PINNED_MEMORY; + else if(nvidia && (m_uploadtype & BUFFERSUBDATA)) + m_uploadtype = BUFFERSUBDATA; + else if(g_ogl_config.bSupportsGLSync && (m_uploadtype & MAP_AND_SYNC)) + m_uploadtype = MAP_AND_SYNC; + else + m_uploadtype = MAP_AND_ORPHAN; + } + + Init(); +} + +StreamBuffer::~StreamBuffer() +{ + Shutdown(); + glDeleteBuffers(1, &m_buffer); +} + +#define SLOT(x) (x)*SYNC_POINTS/m_size + +void StreamBuffer::Alloc ( size_t size, u32 stride ) +{ + size_t m_iterator_aligned = m_iterator; + if(m_iterator_aligned && stride) { + m_iterator_aligned--; + m_iterator_aligned = m_iterator_aligned - (m_iterator_aligned % stride) + stride; + } + size_t iter_end = m_iterator_aligned + size; + + switch(m_uploadtype) { + case MAP_AND_ORPHAN: + if(iter_end >= m_size) { + glBufferData(m_buffertype, m_size, NULL, GL_STREAM_DRAW); + m_iterator_aligned = 0; + } + break; + case MAP_AND_SYNC: + case PINNED_MEMORY: + + // insert waiting slots for used memory + for(u32 i=SLOT(m_used_iterator); i<SLOT(m_iterator); i++) + { + fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } + m_used_iterator = m_iterator; + + // wait for new slots to end of buffer + for(u32 i=SLOT(m_free_iterator)+1; i<=SLOT(iter_end) && i < SYNC_POINTS; i++) + { + glClientWaitSync(fences[i], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); + glDeleteSync(fences[i]); + } + m_free_iterator = iter_end; + + // if buffer is full + if(iter_end >= m_size) { + + // insert waiting slots in unused space at the end of the buffer + for(u32 i=SLOT(m_used_iterator); i < SYNC_POINTS; i++) + fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + + // move to the start + m_used_iterator = m_iterator_aligned = m_iterator = 0; // offset 0 is always aligned + iter_end = size; + + // wait for space at the start + for(u32 i=0; i<=SLOT(iter_end); i++) + { + glClientWaitSync(fences[i], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); + glDeleteSync(fences[i]); + } + m_free_iterator = iter_end; + } + + break; + case MAP_AND_RISK: + if(iter_end >= m_size) { + m_iterator_aligned = 0; + } + break; + case BUFFERSUBDATA: + m_iterator_aligned = 0; + break; + case STREAM_DETECT: + case DETECT_MASK: // Just to shutup warnings + break; + } + m_iterator = m_iterator_aligned; +} + +size_t StreamBuffer::Upload ( u8* data, size_t size ) +{ + switch(m_uploadtype) { + case MAP_AND_SYNC: + case MAP_AND_ORPHAN: + pointer = (u8*)glMapBufferRange(m_buffertype, m_iterator, size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + if(pointer) { + memcpy(pointer, data, size); + glUnmapBuffer(m_buffertype); + } else { + ERROR_LOG(VIDEO, "buffer mapping failed"); + } + break; + case PINNED_MEMORY: + case MAP_AND_RISK: + if(pointer) + memcpy(pointer+m_iterator, data, size); + break; + case BUFFERSUBDATA: + glBufferSubData(m_buffertype, m_iterator, size, data); + break; + case STREAM_DETECT: + case DETECT_MASK: // Just to shutup warnings + break; + } + size_t ret = m_iterator; + m_iterator += size; + return ret; +} + +void StreamBuffer::Init() +{ + m_iterator = 0; + m_used_iterator = 0; + m_free_iterator = 0; + + switch(m_uploadtype) { + case MAP_AND_SYNC: + fences = new GLsync[SYNC_POINTS]; + for(u32 i=0; i<SYNC_POINTS; i++) + fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + + case MAP_AND_ORPHAN: + case BUFFERSUBDATA: + glBindBuffer(m_buffertype, m_buffer); + glBufferData(m_buffertype, m_size, NULL, GL_STREAM_DRAW); + break; + case PINNED_MEMORY: + glGetError(); // errors before this allocation should be ignored + fences = new GLsync[SYNC_POINTS]; + for(u32 i=0; i<SYNC_POINTS; i++) + fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + + pointer = (u8*)AllocateAlignedMemory(ROUND_UP(m_size,ALIGN_PINNED_MEMORY), ALIGN_PINNED_MEMORY ); + glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, m_buffer); + glBufferData(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, ROUND_UP(m_size,ALIGN_PINNED_MEMORY), pointer, GL_STREAM_COPY); + glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0); + glBindBuffer(m_buffertype, m_buffer); + + // on error, switch to another backend. some old catalyst seems to have broken pinned memory support + if(glGetError() != GL_NO_ERROR) { + ERROR_LOG(VIDEO, "pinned memory detected, but not working. Please report this: %s, %s, %s", g_ogl_config.gl_vendor, g_ogl_config.gl_renderer, g_ogl_config.gl_version); + Shutdown(); + m_uploadtype = MAP_AND_SYNC; + Init(); + } + break; + case MAP_AND_RISK: + glBindBuffer(m_buffertype, m_buffer); + glBufferData(m_buffertype, m_size, NULL, GL_STREAM_DRAW); + pointer = (u8*)glMapBuffer(m_buffertype, GL_WRITE_ONLY); + glUnmapBuffer(m_buffertype); + if(!pointer) + ERROR_LOG(VIDEO, "buffer allocation failed"); + + case STREAM_DETECT: + case DETECT_MASK: // Just to shutup warnings + break; + } +} + +void StreamBuffer::Shutdown() +{ + switch(m_uploadtype) { + case MAP_AND_SYNC: + for(u32 i=0; i<SYNC_POINTS; i++) + glDeleteSync(fences[i]); + delete [] fences; + break; + + case MAP_AND_RISK: + case MAP_AND_ORPHAN: + case BUFFERSUBDATA: + break; + case PINNED_MEMORY: + for(u32 i=0; i<SYNC_POINTS; i++) + glDeleteSync(fences[i]); + delete [] fences; + glBindBuffer(m_buffertype, 0); + glFinish(); // ogl pipeline must be flushed, else this buffer can be in use + FreeAlignedMemory(pointer); + break; + case STREAM_DETECT: + case DETECT_MASK: // Just to shutup warnings + break; + } +} + +} |
