mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-09-24 13:35:49 +02:00
This pull request is for the pending reorganization of the folder structure on GSdx, making it better organized and easier to work with. Also remove unused GSTextureFX.cpp file.
341 lines
8.6 KiB
C++
341 lines
8.6 KiB
C++
/*
|
|
* 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
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA USA.
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "config.h"
|
|
|
|
#ifdef ENABLE_OGL_DEBUG_MEM_BW
|
|
extern uint64 g_vertex_upload_byte;
|
|
#endif
|
|
|
|
struct GSInputLayoutOGL {
|
|
GLint location;
|
|
GLint size;
|
|
GLenum type;
|
|
GLboolean normalize;
|
|
GLsizei stride;
|
|
const GLvoid* offset;
|
|
};
|
|
|
|
template<int STRIDE>
|
|
class GSBufferOGL {
|
|
size_t m_start;
|
|
size_t m_count;
|
|
size_t m_limit;
|
|
size_t m_quarter_shift;
|
|
const GLenum m_target;
|
|
GLuint m_buffer_name;
|
|
uint8* m_buffer_ptr;
|
|
GLsync m_fence[5];
|
|
|
|
public:
|
|
GSBufferOGL(GLenum target, size_t count)
|
|
: m_start(0)
|
|
, m_count(0)
|
|
, m_limit(0)
|
|
, m_target(target)
|
|
{
|
|
glGenBuffers(1, &m_buffer_name);
|
|
// Warning m_limit is the number of object (not the size in Bytes)
|
|
// Round it to next power of 2
|
|
m_limit = 1u << (1u + (size_t)std::log2(count - 1u));
|
|
m_quarter_shift = (size_t)std::log2(m_limit * STRIDE) - 2;
|
|
|
|
for (size_t i = 0; i < 5; i++) {
|
|
m_fence[i] = 0;
|
|
}
|
|
|
|
// TODO: if we do manually the synchronization, I'm not sure size is important. It worths to investigate it.
|
|
// => bigger buffer => less sync
|
|
bind();
|
|
|
|
if (STRIDE <= 4)
|
|
glObjectLabel(GL_BUFFER, m_buffer_name, -1, "IBO");
|
|
else
|
|
glObjectLabel(GL_BUFFER, m_buffer_name, -1, "VBO");
|
|
|
|
// 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();
|
|
}
|
|
}
|
|
|
|
~GSBufferOGL() {
|
|
for (size_t i = 0; i < 5; i++) {
|
|
glDeleteSync(m_fence[i]);
|
|
}
|
|
glDeleteBuffers(1, &m_buffer_name);
|
|
}
|
|
|
|
void bind()
|
|
{
|
|
glBindBuffer(m_target, m_buffer_name);
|
|
}
|
|
|
|
void* map(size_t count)
|
|
{
|
|
m_count = count;
|
|
|
|
if (m_count >= m_limit)
|
|
throw GSDXErrorGlVertexArrayTooSmall();
|
|
|
|
size_t offset = m_start * STRIDE;
|
|
size_t length = m_count * STRIDE;
|
|
|
|
if (m_count > (m_limit - m_start) ) {
|
|
size_t current_chunk = offset >> m_quarter_shift;
|
|
#ifdef ENABLE_OGL_DEBUG_FENCE
|
|
fprintf(stderr, "%x: Wrap buffer\n", m_target);
|
|
fprintf(stderr, "%x: Insert a fence in chunk %zu\n", m_target, current_chunk);
|
|
#endif
|
|
ASSERT(current_chunk > 0 && current_chunk < 5);
|
|
if (m_fence[current_chunk] == 0) {
|
|
m_fence[current_chunk] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
}
|
|
|
|
// Wrap at startup
|
|
m_start = 0;
|
|
offset = 0;
|
|
|
|
// Only check first chunk
|
|
if (m_fence[0]) {
|
|
#ifdef ENABLE_OGL_DEBUG_FENCE
|
|
GLenum status = glClientWaitSync(m_fence[0], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
|
if (status != GL_ALREADY_SIGNALED) {
|
|
fprintf(stderr, "%x: Sync Sync! Buffer too small\n", m_target);
|
|
}
|
|
#else
|
|
glClientWaitSync(m_fence[0], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
|
#endif
|
|
glDeleteSync(m_fence[0]);
|
|
m_fence[0] = 0;
|
|
}
|
|
}
|
|
|
|
// Protect buffer with fences
|
|
size_t current_chunk = offset >> m_quarter_shift;
|
|
size_t next_chunk = (offset + length) >> m_quarter_shift;
|
|
for (size_t c = current_chunk + 1; c <= next_chunk; c++) {
|
|
#ifdef ENABLE_OGL_DEBUG_FENCE
|
|
fprintf(stderr, "%x: Insert a fence in chunk %d\n", m_target, c-1);
|
|
#endif
|
|
ASSERT(c > 0 && c < 5);
|
|
m_fence[c-1] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
if (m_fence[c]) {
|
|
#ifdef ENABLE_OGL_DEBUG_FENCE
|
|
GLenum status = glClientWaitSync(m_fence[c], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
|
#else
|
|
glClientWaitSync(m_fence[c], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
|
#endif
|
|
glDeleteSync(m_fence[c]);
|
|
m_fence[c] = 0;
|
|
|
|
#ifdef ENABLE_OGL_DEBUG_FENCE
|
|
if (status != GL_ALREADY_SIGNALED) {
|
|
fprintf(stderr, "%x: Sync Sync! Buffer too small\n", m_target);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return m_buffer_ptr + offset;
|
|
}
|
|
|
|
void unmap()
|
|
{
|
|
glFlushMappedBufferRange(m_target, m_start * STRIDE, m_count * STRIDE);
|
|
}
|
|
|
|
void upload(const void* src, size_t count)
|
|
{
|
|
#ifdef ENABLE_OGL_DEBUG_MEM_BW
|
|
g_vertex_upload_byte += count * STRIDE;
|
|
#endif
|
|
|
|
void* dst = map(count);
|
|
memcpy(dst, src, count * STRIDE);
|
|
unmap();
|
|
}
|
|
|
|
void EndScene()
|
|
{
|
|
m_start += m_count;
|
|
m_count = 0;
|
|
}
|
|
|
|
void Draw(GLenum mode)
|
|
{
|
|
glDrawArrays(mode, m_start, m_count);
|
|
}
|
|
|
|
void Draw(GLenum mode, int offset, int count)
|
|
{
|
|
glDrawArrays(mode, m_start + offset, count);
|
|
}
|
|
|
|
|
|
void Draw(GLenum mode, GLint basevertex)
|
|
{
|
|
glDrawElementsBaseVertex(mode, m_count, GL_UNSIGNED_INT, (void*)(m_start * STRIDE), basevertex);
|
|
}
|
|
|
|
void Draw(GLenum mode, GLint basevertex, int offset, int count)
|
|
{
|
|
glDrawElementsBaseVertex(mode, count, GL_UNSIGNED_INT, (void*)((m_start + offset) * STRIDE), basevertex);
|
|
}
|
|
|
|
size_t GetStart() { return m_start; }
|
|
|
|
};
|
|
|
|
class GSVertexBufferStateOGL {
|
|
std::unique_ptr<GSBufferOGL<sizeof(GSVertexPT1)>> m_vb;
|
|
std::unique_ptr<GSBufferOGL<sizeof(uint32)>> m_ib;
|
|
|
|
GLuint m_va;
|
|
GLenum m_topology;
|
|
std::vector<GSInputLayoutOGL> m_layout;
|
|
|
|
// No copy constructor please
|
|
GSVertexBufferStateOGL(const GSVertexBufferStateOGL& ) = delete;
|
|
|
|
public:
|
|
GSVertexBufferStateOGL(const std::vector<GSInputLayoutOGL>& layout) : m_topology(0), m_layout(layout)
|
|
{
|
|
glGenVertexArrays(1, &m_va);
|
|
glBindVertexArray(m_va);
|
|
|
|
m_vb.reset(new GSBufferOGL<sizeof(GSVertexPT1)>(GL_ARRAY_BUFFER, 256 * 1024));
|
|
m_ib.reset(new GSBufferOGL<sizeof(uint32)>(GL_ELEMENT_ARRAY_BUFFER, 2 * 1024 * 1024));
|
|
|
|
m_vb->bind();
|
|
m_ib->bind();
|
|
|
|
set_internal_format();
|
|
}
|
|
|
|
void bind()
|
|
{
|
|
// Note: index array are part of the VA state so it need to be bound only once.
|
|
glBindVertexArray(m_va);
|
|
if (m_vb)
|
|
m_vb->bind();
|
|
}
|
|
|
|
void set_internal_format()
|
|
{
|
|
for (const auto &l : m_layout) {
|
|
// Note this function need both a vertex array object and a GL_ARRAY_BUFFER buffer
|
|
glEnableVertexAttribArray(l.location);
|
|
switch (l.type) {
|
|
case GL_UNSIGNED_SHORT:
|
|
case GL_UNSIGNED_INT:
|
|
if (l.normalize) {
|
|
glVertexAttribPointer(l.location, l.size, l.type, l.normalize, l.stride, l.offset);
|
|
} else {
|
|
// Rule: when shader use integral (not normalized) you must use glVertexAttribIPointer (note the extra I)
|
|
glVertexAttribIPointer(l.location, l.size, l.type, l.stride, l.offset);
|
|
}
|
|
break;
|
|
default:
|
|
glVertexAttribPointer(l.location, l.size, l.type, l.normalize, l.stride, l.offset);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EndScene()
|
|
{
|
|
m_vb->EndScene();
|
|
m_ib->EndScene();
|
|
}
|
|
|
|
void DrawPrimitive() { m_vb->Draw(m_topology); }
|
|
|
|
void DrawPrimitive(int offset, int count) { m_vb->Draw(m_topology, offset, count); }
|
|
|
|
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* MapVB(size_t count) {
|
|
void *ptr;
|
|
while (true) {
|
|
try {
|
|
ptr = m_vb->map(count);
|
|
break;
|
|
} catch (GSDXErrorGlVertexArrayTooSmall) {
|
|
GL_INS("GL vertex buffer is too small");
|
|
|
|
m_vb.reset(new GSBufferOGL<sizeof(GSVertexPT1)>(GL_ARRAY_BUFFER, count));
|
|
|
|
set_internal_format();
|
|
}
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
void UnmapVB() { m_vb->unmap(); }
|
|
void UploadVB(const void* vertices, size_t count) {
|
|
while (true) {
|
|
try {
|
|
m_vb->upload(vertices, count);
|
|
break;
|
|
} catch (GSDXErrorGlVertexArrayTooSmall) {
|
|
GL_INS("GL vertex buffer is too small");
|
|
|
|
m_vb.reset(new GSBufferOGL<sizeof(GSVertexPT1)>(GL_ARRAY_BUFFER, count));
|
|
|
|
set_internal_format();
|
|
}
|
|
}
|
|
}
|
|
|
|
void UploadIB(const void* index, size_t count) {
|
|
while (true) {
|
|
try {
|
|
m_ib->upload(index, count);
|
|
break;
|
|
} catch (GSDXErrorGlVertexArrayTooSmall) {
|
|
GL_INS("GL index buffer is too small");
|
|
|
|
m_ib.reset(new GSBufferOGL<sizeof(uint32)>(GL_ELEMENT_ARRAY_BUFFER, count));
|
|
}
|
|
}
|
|
}
|
|
|
|
~GSVertexBufferStateOGL()
|
|
{
|
|
glDeleteVertexArrays(1, &m_va);
|
|
}
|
|
|
|
};
|