2012-05-13 17:09:18 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2011-2011 Gregory hainaut
|
|
|
|
* Copyright (C) 2007-2009 Gabest
|
|
|
|
*
|
|
|
|
* 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; either version 2, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* 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 for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with GNU Make; see the file COPYING. If not, write to
|
2012-09-09 18:16:11 +00:00
|
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA USA.
|
2012-05-13 17:09:18 +00:00
|
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2014-03-24 14:03:02 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#ifdef ENABLE_OGL_DEBUG_MEM_BW
|
2015-04-25 12:18:21 +00:00
|
|
|
extern uint64 g_vertex_upload_byte;
|
2014-03-24 14:03:02 +00:00
|
|
|
#endif
|
|
|
|
|
2012-05-13 17:09:18 +00:00
|
|
|
struct GSInputLayoutOGL {
|
|
|
|
GLint size;
|
|
|
|
GLenum type;
|
|
|
|
GLboolean normalize;
|
|
|
|
GLsizei stride;
|
|
|
|
const GLvoid* offset;
|
|
|
|
};
|
|
|
|
|
2016-04-04 20:46:31 +00:00
|
|
|
template<int STRIDE>
|
2012-05-13 17:22:36 +00:00
|
|
|
class GSBufferOGL {
|
|
|
|
size_t m_start;
|
|
|
|
size_t m_count;
|
|
|
|
size_t m_limit;
|
2014-03-24 09:49:45 +00:00
|
|
|
const GLenum m_target;
|
|
|
|
GLuint m_buffer_name;
|
|
|
|
uint8* m_buffer_ptr;
|
2015-04-24 21:11:44 +00:00
|
|
|
GLsync m_fence[5];
|
2012-05-13 17:22:36 +00:00
|
|
|
|
2014-03-24 09:49:45 +00:00
|
|
|
public:
|
2016-04-04 20:46:31 +00:00
|
|
|
GSBufferOGL(GLenum target)
|
|
|
|
: m_start(0)
|
2012-05-13 17:22:36 +00:00
|
|
|
, m_count(0)
|
|
|
|
, m_limit(0)
|
|
|
|
, m_target(target)
|
|
|
|
{
|
2015-10-17 15:05:15 +00:00
|
|
|
glGenBuffers(1, &m_buffer_name);
|
2012-05-13 17:22:36 +00:00
|
|
|
// Opengl works best with 1-4MB buffer.
|
2013-08-17 09:05:41 +00:00
|
|
|
// Warning m_limit is the number of object (not the size in Bytes)
|
2016-04-04 20:46:31 +00:00
|
|
|
m_limit = 8 * 1024 * 1024 / STRIDE;
|
2014-03-24 09:49:45 +00:00
|
|
|
|
2015-09-11 10:19:49 +00:00
|
|
|
for (size_t i = 0; i < 5; i++) {
|
|
|
|
m_fence[i] = 0;
|
|
|
|
}
|
2015-04-24 21:11:44 +00:00
|
|
|
|
2016-04-07 20:17:22 +00:00
|
|
|
// TODO: if we do manually the synchronization, I'm not sure size is important. It worths to investigate it.
|
|
|
|
// => bigger buffer => less sync
|
|
|
|
bind();
|
2016-06-07 18:30:56 +00:00
|
|
|
|
|
|
|
if (STRIDE <= 4)
|
|
|
|
glObjectLabel(GL_BUFFER, m_buffer_name, -1, "IBO");
|
|
|
|
else
|
|
|
|
glObjectLabel(GL_BUFFER, m_buffer_name, -1, "VBO");
|
|
|
|
|
2016-04-07 20:17:22 +00:00
|
|
|
// coherency will be done by flushing
|
|
|
|
const GLbitfield common_flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
|
|
|
|
const GLbitfield map_flags = common_flags | GL_MAP_FLUSH_EXPLICIT_BIT;
|
|
|
|
const GLbitfield create_flags = common_flags | GL_CLIENT_STORAGE_BIT;
|
|
|
|
|
|
|
|
glBufferStorage(m_target, STRIDE * m_limit, NULL, create_flags );
|
|
|
|
m_buffer_ptr = (uint8*) glMapBufferRange(m_target, 0, STRIDE * m_limit, map_flags);
|
|
|
|
if (!m_buffer_ptr) {
|
|
|
|
fprintf(stderr, "Failed to map buffer\n");
|
|
|
|
throw GSDXError();
|
2014-03-24 09:49:45 +00:00
|
|
|
}
|
2012-05-13 17:22:36 +00:00
|
|
|
}
|
2012-05-13 17:09:18 +00:00
|
|
|
|
2014-03-24 09:49:45 +00:00
|
|
|
~GSBufferOGL() {
|
2016-04-07 20:17:22 +00:00
|
|
|
for (size_t i = 0; i < 5; i++) {
|
|
|
|
glDeleteSync(m_fence[i]);
|
2014-03-24 09:49:45 +00:00
|
|
|
}
|
2016-04-07 20:17:22 +00:00
|
|
|
// Don't know if we must do it
|
|
|
|
bind();
|
|
|
|
glUnmapBuffer(m_target);
|
2015-10-17 15:05:15 +00:00
|
|
|
glDeleteBuffers(1, &m_buffer_name);
|
2014-03-24 09:49:45 +00:00
|
|
|
}
|
2012-05-13 17:09:18 +00:00
|
|
|
|
2012-05-13 17:22:36 +00:00
|
|
|
void bind()
|
|
|
|
{
|
2015-10-17 15:05:15 +00:00
|
|
|
glBindBuffer(m_target, m_buffer_name);
|
2012-05-13 17:22:36 +00:00
|
|
|
}
|
2012-05-13 17:09:18 +00:00
|
|
|
|
2015-04-24 21:11:44 +00:00
|
|
|
void map_upload(const void* src)
|
2012-05-13 17:22:36 +00:00
|
|
|
{
|
2015-05-01 10:16:26 +00:00
|
|
|
ASSERT(m_count < m_limit);
|
2015-04-20 07:25:58 +00:00
|
|
|
|
2016-04-04 20:46:31 +00:00
|
|
|
size_t offset = m_start * STRIDE;
|
|
|
|
size_t length = m_count * STRIDE;
|
2015-05-01 10:16:26 +00:00
|
|
|
|
|
|
|
if (m_count > (m_limit - m_start) ) {
|
2015-06-04 06:21:22 +00:00
|
|
|
size_t current_chunk = offset >> 21;
|
2015-04-24 21:11:44 +00:00
|
|
|
#ifdef ENABLE_OGL_DEBUG_FENCE
|
2015-05-01 10:16:26 +00:00
|
|
|
fprintf(stderr, "%x: Wrap buffer\n", m_target);
|
2016-08-13 10:40:30 +00:00
|
|
|
fprintf(stderr, "%x: Insert a fence in chunk %zu\n", m_target, current_chunk);
|
2015-04-24 21:11:44 +00:00
|
|
|
#endif
|
2015-05-01 10:16:26 +00:00
|
|
|
ASSERT(current_chunk > 0 && current_chunk < 5);
|
|
|
|
if (m_fence[current_chunk] == 0) {
|
2015-10-17 15:05:15 +00:00
|
|
|
m_fence[current_chunk] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
2015-05-01 10:16:26 +00:00
|
|
|
}
|
2015-04-24 21:11:44 +00:00
|
|
|
|
2015-05-01 10:16:26 +00:00
|
|
|
// Wrap at startup
|
|
|
|
m_start = 0;
|
|
|
|
offset = 0;
|
2015-04-24 21:11:44 +00:00
|
|
|
|
2015-05-01 10:16:26 +00:00
|
|
|
// Only check first chunk
|
|
|
|
if (m_fence[0]) {
|
2015-04-24 21:11:44 +00:00
|
|
|
#ifdef ENABLE_OGL_DEBUG_FENCE
|
2015-10-17 15:05:15 +00:00
|
|
|
GLenum status = glClientWaitSync(m_fence[0], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
2015-05-01 10:16:26 +00:00
|
|
|
if (status != GL_ALREADY_SIGNALED) {
|
|
|
|
fprintf(stderr, "%x: Sync Sync! Buffer too small\n", m_target);
|
|
|
|
}
|
2015-04-24 21:11:44 +00:00
|
|
|
#else
|
2015-10-17 15:05:15 +00:00
|
|
|
glClientWaitSync(m_fence[0], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
2015-04-24 21:11:44 +00:00
|
|
|
#endif
|
2015-10-17 15:05:15 +00:00
|
|
|
glDeleteSync(m_fence[0]);
|
2015-05-01 10:16:26 +00:00
|
|
|
m_fence[0] = 0;
|
2015-04-24 21:11:44 +00:00
|
|
|
}
|
2015-05-01 10:16:26 +00:00
|
|
|
}
|
2015-04-24 21:11:44 +00:00
|
|
|
|
2015-05-01 10:16:26 +00:00
|
|
|
// Protect buffer with fences
|
2015-06-04 06:21:22 +00:00
|
|
|
size_t current_chunk = offset >> 21;
|
|
|
|
size_t next_chunk = (offset + length) >> 21;
|
2015-05-01 10:16:26 +00:00
|
|
|
for (size_t c = current_chunk + 1; c <= next_chunk; c++) {
|
2015-04-24 21:11:44 +00:00
|
|
|
#ifdef ENABLE_OGL_DEBUG_FENCE
|
2015-05-01 10:16:26 +00:00
|
|
|
fprintf(stderr, "%x: Insert a fence in chunk %d\n", m_target, c-1);
|
2015-04-24 21:11:44 +00:00
|
|
|
#endif
|
2015-05-01 10:16:26 +00:00
|
|
|
ASSERT(c > 0 && c < 5);
|
2015-10-17 15:05:15 +00:00
|
|
|
m_fence[c-1] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
2015-05-01 10:16:26 +00:00
|
|
|
if (m_fence[c]) {
|
2015-04-24 21:11:44 +00:00
|
|
|
#ifdef ENABLE_OGL_DEBUG_FENCE
|
2015-10-17 15:05:15 +00:00
|
|
|
GLenum status = glClientWaitSync(m_fence[c], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
2015-04-24 21:11:44 +00:00
|
|
|
#else
|
2015-10-17 15:05:15 +00:00
|
|
|
glClientWaitSync(m_fence[c], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
2015-04-24 21:11:44 +00:00
|
|
|
#endif
|
2015-10-17 15:05:15 +00:00
|
|
|
glDeleteSync(m_fence[c]);
|
2015-05-01 10:16:26 +00:00
|
|
|
m_fence[c] = 0;
|
2015-04-24 21:11:44 +00:00
|
|
|
|
|
|
|
#ifdef ENABLE_OGL_DEBUG_FENCE
|
2015-05-01 10:16:26 +00:00
|
|
|
if (status != GL_ALREADY_SIGNALED) {
|
|
|
|
fprintf(stderr, "%x: Sync Sync! Buffer too small\n", m_target);
|
2015-04-24 21:11:44 +00:00
|
|
|
}
|
2015-05-01 10:16:26 +00:00
|
|
|
#endif
|
2014-03-24 09:49:45 +00:00
|
|
|
}
|
2015-04-20 07:25:58 +00:00
|
|
|
}
|
|
|
|
|
2015-05-01 10:16:26 +00:00
|
|
|
void* dst = m_buffer_ptr + offset;
|
|
|
|
|
2015-04-21 19:44:50 +00:00
|
|
|
memcpy(dst, src, length);
|
2015-10-17 15:05:15 +00:00
|
|
|
glFlushMappedBufferRange(m_target, offset, length);
|
2012-05-13 17:22:36 +00:00
|
|
|
}
|
2012-05-13 17:09:18 +00:00
|
|
|
|
2013-06-26 20:09:07 +00:00
|
|
|
void upload(const void* src, uint32 count)
|
|
|
|
{
|
2014-03-24 14:03:02 +00:00
|
|
|
#ifdef ENABLE_OGL_DEBUG_MEM_BW
|
2016-04-04 20:46:31 +00:00
|
|
|
g_vertex_upload_byte += count * STRIDE;
|
2013-07-12 21:12:34 +00:00
|
|
|
#endif
|
2015-04-24 21:11:44 +00:00
|
|
|
|
|
|
|
m_count = count;
|
|
|
|
|
2016-04-07 20:17:22 +00:00
|
|
|
map_upload(src);
|
2014-03-24 09:49:45 +00:00
|
|
|
}
|
2012-05-13 17:09:18 +00:00
|
|
|
|
2012-05-13 17:22:36 +00:00
|
|
|
void EndScene()
|
|
|
|
{
|
|
|
|
m_start += m_count;
|
|
|
|
m_count = 0;
|
|
|
|
}
|
2012-05-13 17:09:18 +00:00
|
|
|
|
2012-05-13 17:22:36 +00:00
|
|
|
void Draw(GLenum mode)
|
|
|
|
{
|
|
|
|
glDrawArrays(mode, m_start, m_count);
|
|
|
|
}
|
2012-05-13 17:09:18 +00:00
|
|
|
|
2015-08-01 11:33:30 +00:00
|
|
|
void Draw(GLenum mode, int offset, int count)
|
|
|
|
{
|
|
|
|
glDrawArrays(mode, m_start + offset, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-13 17:22:36 +00:00
|
|
|
void Draw(GLenum mode, GLint basevertex)
|
|
|
|
{
|
2016-04-04 20:46:31 +00:00
|
|
|
glDrawElementsBaseVertex(mode, m_count, GL_UNSIGNED_INT, (void*)(m_start * STRIDE), basevertex);
|
2012-05-13 17:22:36 +00:00
|
|
|
}
|
2012-05-13 17:09:18 +00:00
|
|
|
|
2012-05-13 17:22:36 +00:00
|
|
|
void Draw(GLenum mode, GLint basevertex, int offset, int count)
|
|
|
|
{
|
2016-04-04 20:46:31 +00:00
|
|
|
glDrawElementsBaseVertex(mode, count, GL_UNSIGNED_INT, (void*)((m_start + offset) * STRIDE), basevertex);
|
2012-05-13 17:22:36 +00:00
|
|
|
}
|
2012-05-13 17:09:18 +00:00
|
|
|
|
2012-05-13 17:22:36 +00:00
|
|
|
size_t GetStart() { return m_start; }
|
2012-05-13 17:09:18 +00:00
|
|
|
|
2012-05-13 17:22:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class GSVertexBufferStateOGL {
|
2016-04-04 20:46:31 +00:00
|
|
|
GSBufferOGL<sizeof(GSVertexPT1)> *m_vb;
|
|
|
|
GSBufferOGL<sizeof(uint32)> *m_ib;
|
2012-05-13 17:09:18 +00:00
|
|
|
|
|
|
|
GLuint m_va;
|
|
|
|
GLenum m_topology;
|
|
|
|
|
2015-11-06 21:53:30 +00:00
|
|
|
// No copy constructor please
|
|
|
|
GSVertexBufferStateOGL(const GSVertexBufferStateOGL& ) = delete;
|
|
|
|
|
2012-05-13 17:09:18 +00:00
|
|
|
public:
|
2016-04-04 20:46:31 +00:00
|
|
|
GSVertexBufferStateOGL(GSInputLayoutOGL* layout, uint32 layout_nbr) : m_vb(NULL), m_ib(NULL), m_topology(0)
|
2012-05-13 17:09:18 +00:00
|
|
|
{
|
2015-10-17 15:05:15 +00:00
|
|
|
glGenVertexArrays(1, &m_va);
|
|
|
|
glBindVertexArray(m_va);
|
2012-05-13 17:09:18 +00:00
|
|
|
|
2016-04-04 20:46:31 +00:00
|
|
|
m_vb = new GSBufferOGL<sizeof(GSVertexPT1)>(GL_ARRAY_BUFFER);
|
|
|
|
m_ib = new GSBufferOGL<sizeof(uint32)>(GL_ELEMENT_ARRAY_BUFFER);
|
2012-05-13 17:09:18 +00:00
|
|
|
|
2015-04-26 09:01:55 +00:00
|
|
|
m_vb->bind();
|
2012-05-13 17:09:18 +00:00
|
|
|
m_ib->bind();
|
|
|
|
|
|
|
|
set_internal_format(layout, layout_nbr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void bind()
|
|
|
|
{
|
2015-04-26 09:01:55 +00:00
|
|
|
// Note: index array are part of the VA state so it need to be bound only once.
|
2015-10-17 15:05:15 +00:00
|
|
|
glBindVertexArray(m_va);
|
2015-04-25 10:48:47 +00:00
|
|
|
if (m_vb)
|
|
|
|
m_vb->bind();
|
2012-05-13 17:09:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void set_internal_format(GSInputLayoutOGL* layout, uint32 layout_nbr)
|
|
|
|
{
|
2013-01-10 13:13:59 +00:00
|
|
|
for (uint32 i = 0; i < layout_nbr; i++) {
|
2012-05-13 17:09:18 +00:00
|
|
|
// Note this function need both a vertex array object and a GL_ARRAY_BUFFER buffer
|
2015-10-17 15:05:15 +00:00
|
|
|
glEnableVertexAttribArray(i);
|
2012-05-13 17:09:18 +00:00
|
|
|
switch (layout[i].type) {
|
|
|
|
case GL_UNSIGNED_SHORT:
|
|
|
|
case GL_UNSIGNED_INT:
|
2014-01-31 21:30:54 +00:00
|
|
|
if (layout[i].normalize) {
|
2015-10-17 15:05:15 +00:00
|
|
|
glVertexAttribPointer(i, layout[i].size, layout[i].type, layout[i].normalize, layout[i].stride, layout[i].offset);
|
2014-01-31 21:30:54 +00:00
|
|
|
} else {
|
2015-10-17 15:05:15 +00:00
|
|
|
// Rule: when shader use integral (not normalized) you must use glVertexAttribIPointer (note the extra I)
|
|
|
|
glVertexAttribIPointer(i, layout[i].size, layout[i].type, layout[i].stride, layout[i].offset);
|
2014-01-31 21:30:54 +00:00
|
|
|
}
|
2012-05-13 17:09:18 +00:00
|
|
|
break;
|
|
|
|
default:
|
2015-10-17 15:05:15 +00:00
|
|
|
glVertexAttribPointer(i, layout[i].size, layout[i].type, layout[i].normalize, layout[i].stride, layout[i].offset);
|
2012-05-13 17:09:18 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EndScene()
|
|
|
|
{
|
|
|
|
m_vb->EndScene();
|
|
|
|
m_ib->EndScene();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DrawPrimitive() { m_vb->Draw(m_topology); }
|
|
|
|
|
2015-08-01 11:33:30 +00:00
|
|
|
void DrawPrimitive(int offset, int count) { m_vb->Draw(m_topology, offset, count); }
|
|
|
|
|
2012-05-13 17:09:18 +00:00
|
|
|
void DrawIndexedPrimitive() { m_ib->Draw(m_topology, m_vb->GetStart() ); }
|
|
|
|
|
|
|
|
void DrawIndexedPrimitive(int offset, int count) { m_ib->Draw(m_topology, m_vb->GetStart(), offset, count ); }
|
|
|
|
|
|
|
|
void SetTopology(GLenum topology) { m_topology = topology; }
|
|
|
|
|
|
|
|
void UploadVB(const void* vertices, size_t count) { m_vb->upload(vertices, count); }
|
|
|
|
|
2013-07-12 21:12:34 +00:00
|
|
|
void UploadIB(const void* index, size_t count) {
|
|
|
|
m_ib->upload(index, count);
|
|
|
|
}
|
2012-05-13 17:09:18 +00:00
|
|
|
|
|
|
|
~GSVertexBufferStateOGL()
|
|
|
|
{
|
2015-10-17 15:05:15 +00:00
|
|
|
glDeleteVertexArrays(1, &m_va);
|
2012-05-13 17:22:36 +00:00
|
|
|
delete m_vb;
|
|
|
|
delete m_ib;
|
2012-05-13 17:09:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
};
|