mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-02-03 19:41:45 +02:00
ba8264c2ac
to use VAO, we must use VBO, so some legency code was removed: - ARB_map_buffer_range must be available (OGL 3.0), don't call glBufferSubData if not - ARB_draw_elements_base_vertex also (OGL 3.2), else we have to set the pointers every time - USE_JIT was removed, it was broken and it isn't needed any more And the index and vertex buffers are now synchronized, so that there will be one VAO per NativeVertexFormat and Buffer.
427 lines
14 KiB
C++
427 lines
14 KiB
C++
// 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 <fstream>
|
|
#include <vector>
|
|
|
|
#include "Fifo.h"
|
|
|
|
#include "VideoConfig.h"
|
|
#include "Statistics.h"
|
|
#include "MemoryUtil.h"
|
|
#include "Render.h"
|
|
#include "ImageWrite.h"
|
|
#include "BPMemory.h"
|
|
#include "TextureCache.h"
|
|
#include "PixelShaderCache.h"
|
|
#include "PixelShaderManager.h"
|
|
#include "VertexShaderCache.h"
|
|
#include "VertexShaderManager.h"
|
|
#include "VertexShaderGen.h"
|
|
#include "VertexLoader.h"
|
|
#include "VertexManager.h"
|
|
#include "IndexGenerator.h"
|
|
#include "OpcodeDecoding.h"
|
|
#include "FileUtil.h"
|
|
#include "Debugger.h"
|
|
|
|
#include "main.h"
|
|
|
|
// internal state for loading vertices
|
|
extern NativeVertexFormat *g_nativeVertexFmt;
|
|
|
|
namespace OGL
|
|
{
|
|
//This are the initially requeted size for the buffers expresed in bytes
|
|
const u32 MAX_IBUFFER_SIZE = VertexManager::MAXIBUFFERSIZE * 16 * sizeof(u16);
|
|
const u32 MAX_VBUFFER_SIZE = VertexManager::MAXVBUFFERSIZE * 16;
|
|
const u32 MIN_IBUFFER_SIZE = VertexManager::MAXIBUFFERSIZE * 1 * sizeof(u16);
|
|
const u32 MIN_VBUFFER_SIZE = VertexManager::MAXVBUFFERSIZE * 1;
|
|
const u32 MAX_VBUFFER_COUNT = 2;
|
|
|
|
VertexManager::VertexManager()
|
|
{
|
|
CreateDeviceObjects();
|
|
}
|
|
|
|
VertexManager::~VertexManager()
|
|
{
|
|
DestroyDeviceObjects();
|
|
}
|
|
|
|
void VertexManager::CreateDeviceObjects()
|
|
{
|
|
m_buffers_count = 0;
|
|
m_vertex_buffers = NULL;
|
|
m_index_buffers = NULL;
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
GL_REPORT_ERRORD();
|
|
u32 max_Index_size = 0;
|
|
u32 max_Vertex_size = 0;
|
|
glGetIntegerv(GL_MAX_ELEMENTS_INDICES, (GLint*)&max_Index_size);
|
|
glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, (GLint*)&max_Vertex_size);
|
|
max_Index_size *= sizeof(u16);
|
|
GL_REPORT_ERROR();
|
|
|
|
m_index_buffer_size = std::min(MAX_IBUFFER_SIZE, std::max(max_Index_size, MIN_IBUFFER_SIZE));
|
|
m_vertex_buffer_size = std::min(MAX_VBUFFER_SIZE, std::max(max_Vertex_size, MIN_VBUFFER_SIZE));
|
|
|
|
// should be not bigger, but we need it. so try and have luck
|
|
if (m_index_buffer_size > max_Index_size) {
|
|
ERROR_LOG(VIDEO, "GL_MAX_ELEMENTS_INDICES to small, so try it anyway. good luck\n");
|
|
}
|
|
if (m_vertex_buffer_size > max_Vertex_size) {
|
|
ERROR_LOG(VIDEO, "GL_MAX_ELEMENTS_VERTICES to small, so try it anyway. good luck\n");
|
|
}
|
|
|
|
//TODO: find out, how many buffers fit in gpu memory
|
|
m_buffers_count = MAX_VBUFFER_COUNT;
|
|
|
|
m_vertex_buffers = new GLuint[m_buffers_count];
|
|
m_index_buffers = new GLuint[m_buffers_count];
|
|
|
|
glGenBuffers(m_buffers_count, m_vertex_buffers);
|
|
GL_REPORT_ERROR();
|
|
glGenBuffers(m_buffers_count, m_index_buffers);
|
|
GL_REPORT_ERROR();
|
|
for (u32 i = 0; i < m_buffers_count; i++)
|
|
{
|
|
glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffers[i] );
|
|
GL_REPORT_ERROR();
|
|
glBufferData(GL_ARRAY_BUFFER, m_vertex_buffer_size, NULL, GL_STREAM_DRAW );
|
|
GL_REPORT_ERROR();
|
|
}
|
|
for (u32 i = 0; i < m_buffers_count; i++)
|
|
{
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffers[i] );
|
|
GL_REPORT_ERROR();
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer_size, NULL, GL_STREAM_DRAW );
|
|
GL_REPORT_ERROR();
|
|
}
|
|
m_current_buffer = 0;
|
|
m_index_buffer_cursor = 0;
|
|
m_vertex_buffer_cursor = 0;
|
|
m_CurrentVertexFmt = NULL;
|
|
m_last_vao = 0;
|
|
}
|
|
void VertexManager::DestroyDeviceObjects()
|
|
{
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
GL_REPORT_ERRORD();
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0 );
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0 );
|
|
GL_REPORT_ERROR();
|
|
|
|
glDeleteBuffers(m_buffers_count, m_vertex_buffers);
|
|
GL_REPORT_ERROR();
|
|
delete [] m_vertex_buffers;
|
|
|
|
glDeleteBuffers(m_buffers_count, m_index_buffers);
|
|
GL_REPORT_ERROR();
|
|
delete [] m_index_buffers;
|
|
|
|
m_vertex_buffers = NULL;
|
|
m_index_buffers = NULL;
|
|
m_buffers_count = 0;
|
|
}
|
|
|
|
void VertexManager::PrepareDrawBuffers(u32 stride)
|
|
{
|
|
u8* pVertices = NULL;
|
|
u16* pIndices = NULL;
|
|
int vertex_data_size = IndexGenerator::GetNumVerts() * stride;
|
|
int triangle_index_size = IndexGenerator::GetTriangleindexLen();
|
|
int line_index_size = IndexGenerator::GetLineindexLen();
|
|
int point_index_size = IndexGenerator::GetPointindexLen();
|
|
int index_data_size = (triangle_index_size + line_index_size + point_index_size) * sizeof(u16);
|
|
GLVertexFormat *nativeVertexFmt = (GLVertexFormat*)g_nativeVertexFmt;
|
|
GLbitfield LockMode = GL_MAP_WRITE_BIT;
|
|
|
|
m_vertex_buffer_cursor--;
|
|
m_vertex_buffer_cursor = m_vertex_buffer_cursor - (m_vertex_buffer_cursor % stride) + stride;
|
|
|
|
if (m_vertex_buffer_cursor >= m_vertex_buffer_size - vertex_data_size || m_index_buffer_cursor >= m_index_buffer_size - index_data_size)
|
|
{
|
|
// do we really want to set this? this require a reallocation. usualy only one buffer with reallocation, or much buffers without it
|
|
LockMode |= GL_MAP_INVALIDATE_BUFFER_BIT;
|
|
m_vertex_buffer_cursor = 0;
|
|
m_index_buffer_cursor = 0;
|
|
m_current_buffer = (m_current_buffer + 1) % m_buffers_count;
|
|
glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffers[m_current_buffer]);
|
|
}
|
|
else
|
|
{
|
|
LockMode |= GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
|
|
}
|
|
|
|
// this replaces SetupVertexPointers and must be called after switching buffer and befor uploading indexes
|
|
// but could be deleted, if we only use one buffer with orphaning
|
|
if(m_last_vao != nativeVertexFmt->VAO[m_current_buffer])
|
|
glBindVertexArray(nativeVertexFmt->VAO[m_current_buffer]);
|
|
|
|
pVertices = (u8*)glMapBufferRange(GL_ARRAY_BUFFER, m_vertex_buffer_cursor, vertex_data_size, LockMode);
|
|
if(pVertices)
|
|
{
|
|
memcpy(pVertices, LocalVBuffer, vertex_data_size);
|
|
glUnmapBuffer(GL_ARRAY_BUFFER);
|
|
}
|
|
else // could that happen? out-of-memory?
|
|
{
|
|
glBufferSubData(GL_ARRAY_BUFFER, m_vertex_buffer_cursor, vertex_data_size, LocalVBuffer);
|
|
}
|
|
|
|
pIndices = (u16*)glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer_cursor , index_data_size, LockMode);
|
|
if(pIndices)
|
|
{
|
|
if(triangle_index_size)
|
|
{
|
|
memcpy(pIndices, TIBuffer, triangle_index_size * sizeof(u16));
|
|
pIndices += triangle_index_size;
|
|
}
|
|
if(line_index_size)
|
|
{
|
|
memcpy(pIndices, LIBuffer, line_index_size * sizeof(u16));
|
|
pIndices += line_index_size;
|
|
}
|
|
if(point_index_size)
|
|
{
|
|
memcpy(pIndices, PIBuffer, point_index_size * sizeof(u16));
|
|
}
|
|
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
|
|
}
|
|
else // could that happen? out-of-memory?
|
|
{
|
|
if(triangle_index_size)
|
|
{
|
|
triangle_index_size *= sizeof(u16);
|
|
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer_cursor, triangle_index_size, TIBuffer);
|
|
}
|
|
if(line_index_size)
|
|
{
|
|
line_index_size *= sizeof(u16);
|
|
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer_cursor + triangle_index_size, line_index_size, LIBuffer);
|
|
}
|
|
if(point_index_size)
|
|
{
|
|
point_index_size *= sizeof(u16);
|
|
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer_cursor + triangle_index_size + line_index_size, point_index_size, PIBuffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
void VertexManager::Draw(u32 stride)
|
|
{
|
|
int triangle_index_size = IndexGenerator::GetTriangleindexLen();
|
|
int line_index_size = IndexGenerator::GetLineindexLen();
|
|
int point_index_size = IndexGenerator::GetPointindexLen();
|
|
int StartIndex = m_index_buffer_cursor;
|
|
int basevertex = m_vertex_buffer_cursor / stride;
|
|
if (triangle_index_size > 0)
|
|
{
|
|
glDrawElementsBaseVertex(GL_TRIANGLES, triangle_index_size, GL_UNSIGNED_SHORT, (u8*)NULL+StartIndex, basevertex);
|
|
StartIndex += triangle_index_size * sizeof(u16);
|
|
INCSTAT(stats.thisFrame.numIndexedDrawCalls);
|
|
}
|
|
if (line_index_size > 0)
|
|
{
|
|
glDrawElementsBaseVertex(GL_LINES, line_index_size, GL_UNSIGNED_SHORT, (u8*)NULL+StartIndex, basevertex);
|
|
StartIndex += line_index_size * sizeof(u16);
|
|
INCSTAT(stats.thisFrame.numIndexedDrawCalls);
|
|
}
|
|
if (point_index_size > 0)
|
|
{
|
|
glDrawElementsBaseVertex(GL_POINTS, point_index_size, GL_UNSIGNED_SHORT, (u8*)NULL+StartIndex, basevertex);
|
|
INCSTAT(stats.thisFrame.numIndexedDrawCalls);
|
|
}
|
|
}
|
|
|
|
void VertexManager::vFlush()
|
|
{
|
|
VideoFifo_CheckEFBAccess();
|
|
#if defined(_DEBUG) || defined(DEBUGFAST)
|
|
PRIM_LOG("frame%d:\n texgen=%d, numchan=%d, dualtex=%d, ztex=%d, cole=%d, alpe=%d, ze=%d", g_ActiveConfig.iSaveTargetId, xfregs.numTexGen.numTexGens,
|
|
xfregs.numChan.numColorChans, xfregs.dualTexTrans.enabled, bpmem.ztex2.op,
|
|
bpmem.blendmode.colorupdate, bpmem.blendmode.alphaupdate, bpmem.zmode.updateenable);
|
|
|
|
for (unsigned int i = 0; i < xfregs.numChan.numColorChans; ++i)
|
|
{
|
|
LitChannel* ch = &xfregs.color[i];
|
|
PRIM_LOG("colchan%d: matsrc=%d, light=0x%x, ambsrc=%d, diffunc=%d, attfunc=%d", i, ch->matsource, ch->GetFullLightMask(), ch->ambsource, ch->diffusefunc, ch->attnfunc);
|
|
ch = &xfregs.alpha[i];
|
|
PRIM_LOG("alpchan%d: matsrc=%d, light=0x%x, ambsrc=%d, diffunc=%d, attfunc=%d", i, ch->matsource, ch->GetFullLightMask(), ch->ambsource, ch->diffusefunc, ch->attnfunc);
|
|
}
|
|
|
|
for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i)
|
|
{
|
|
TexMtxInfo tinfo = xfregs.texMtxInfo[i];
|
|
if (tinfo.texgentype != XF_TEXGEN_EMBOSS_MAP) tinfo.hex &= 0x7ff;
|
|
if (tinfo.texgentype != XF_TEXGEN_REGULAR) tinfo.projection = 0;
|
|
|
|
PRIM_LOG("txgen%d: proj=%d, input=%d, gentype=%d, srcrow=%d, embsrc=%d, emblght=%d, postmtx=%d, postnorm=%d",
|
|
i, tinfo.projection, tinfo.inputform, tinfo.texgentype, tinfo.sourcerow, tinfo.embosssourceshift, tinfo.embosslightshift,
|
|
xfregs.postMtxInfo[i].index, xfregs.postMtxInfo[i].normalize);
|
|
}
|
|
|
|
PRIM_LOG("pixel: tev=%d, ind=%d, texgen=%d, dstalpha=%d, alphafunc=0x%x", bpmem.genMode.numtevstages+1, bpmem.genMode.numindstages,
|
|
bpmem.genMode.numtexgens, (u32)bpmem.dstalpha.enable, (bpmem.alphaFunc.hex>>16)&0xff);
|
|
#endif
|
|
|
|
(void)GL_REPORT_ERROR();
|
|
|
|
u32 stride = g_nativeVertexFmt->GetVertexStride();
|
|
|
|
PrepareDrawBuffers(stride);
|
|
GL_REPORT_ERRORD();
|
|
|
|
u32 usedtextures = 0;
|
|
for (u32 i = 0; i < (u32)bpmem.genMode.numtevstages + 1; ++i)
|
|
if (bpmem.tevorders[i / 2].getEnable(i & 1))
|
|
usedtextures |= 1 << bpmem.tevorders[i/2].getTexMap(i & 1);
|
|
|
|
if (bpmem.genMode.numindstages > 0)
|
|
for (u32 i = 0; i < (u32)bpmem.genMode.numtevstages + 1; ++i)
|
|
if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages)
|
|
usedtextures |= 1 << bpmem.tevindref.getTexMap(bpmem.tevind[i].bt);
|
|
|
|
for (u32 i = 0; i < 8; i++)
|
|
{
|
|
if (usedtextures & (1 << i))
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + i);
|
|
FourTexUnits &tex = bpmem.tex[i >> 2];
|
|
TextureCache::TCacheEntryBase* tentry = TextureCache::Load(i,
|
|
(tex.texImage3[i&3].image_base/* & 0x1FFFFF*/) << 5,
|
|
tex.texImage0[i&3].width + 1, tex.texImage0[i&3].height + 1,
|
|
tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9,
|
|
tex.texTlut[i&3].tlut_format,
|
|
(tex.texMode0[i&3].min_filter & 3) && (tex.texMode0[i&3].min_filter != 8),
|
|
tex.texMode1[i&3].max_lod >> 4,
|
|
tex.texImage1[i&3].image_type);
|
|
|
|
if (tentry)
|
|
{
|
|
// 0s are probably for no manual wrapping needed.
|
|
PixelShaderManager::SetTexDims(i, tentry->native_width, tentry->native_height, 0, 0);
|
|
}
|
|
else
|
|
ERROR_LOG(VIDEO, "error loading texture");
|
|
}
|
|
}
|
|
|
|
// set global constants
|
|
VertexShaderManager::SetConstants();
|
|
PixelShaderManager::SetConstants();
|
|
|
|
bool useDstAlpha = !g_ActiveConfig.bDstAlphaPass && bpmem.dstalpha.enable && bpmem.blendmode.alphaupdate
|
|
&& bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24;
|
|
|
|
#ifdef USE_DUAL_SOURCE_BLEND
|
|
bool dualSourcePossible = GLEW_ARB_blend_func_extended;
|
|
|
|
// finally bind
|
|
FRAGMENTSHADER* ps;
|
|
if (dualSourcePossible)
|
|
{
|
|
if (useDstAlpha)
|
|
{
|
|
// If host supports GL_ARB_blend_func_extended, we can do dst alpha in
|
|
// the same pass as regular rendering.
|
|
g_renderer->SetBlendMode(true);
|
|
ps = PixelShaderCache::SetShader(DSTALPHA_DUAL_SOURCE_BLEND, g_nativeVertexFmt->m_components);
|
|
}
|
|
else
|
|
{
|
|
g_renderer->SetBlendMode(true);
|
|
ps = PixelShaderCache::SetShader(DSTALPHA_NONE,g_nativeVertexFmt->m_components);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ps = PixelShaderCache::SetShader(DSTALPHA_NONE,g_nativeVertexFmt->m_components);
|
|
}
|
|
#else
|
|
bool dualSourcePossible = false;
|
|
FRAGMENTSHADER* ps = PixelShaderCache::SetShader(DSTALPHA_NONE,g_nativeVertexFmt->m_components);
|
|
#endif
|
|
VERTEXSHADER* vs = VertexShaderCache::SetShader(g_nativeVertexFmt->m_components);
|
|
if (ps) PixelShaderCache::SetCurrentShader(ps->glprogid); // Lego Star Wars crashes here.
|
|
if (vs) VertexShaderCache::SetCurrentShader(vs->glprogid);
|
|
|
|
Draw(stride);
|
|
|
|
// run through vertex groups again to set alpha
|
|
if (useDstAlpha && !dualSourcePossible)
|
|
{
|
|
ps = PixelShaderCache::SetShader(DSTALPHA_ALPHA_PASS,g_nativeVertexFmt->m_components);
|
|
if (ps) PixelShaderCache::SetCurrentShader(ps->glprogid);
|
|
|
|
// only update alpha
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
Draw(stride);
|
|
|
|
// restore color mask
|
|
g_renderer->SetColorMask();
|
|
|
|
if (bpmem.blendmode.blendenable || bpmem.blendmode.subtract)
|
|
glEnable(GL_BLEND);
|
|
}
|
|
GFX_DEBUGGER_PAUSE_AT(NEXT_FLUSH, true);
|
|
if(m_buffers_count)
|
|
{
|
|
m_index_buffer_cursor += (IndexGenerator::GetTriangleindexLen() + IndexGenerator::GetLineindexLen() + IndexGenerator::GetPointindexLen()) * sizeof(u16);
|
|
m_vertex_buffer_cursor += IndexGenerator::GetNumVerts() * stride;
|
|
}
|
|
ResetBuffer();
|
|
#if defined(_DEBUG) || defined(DEBUGFAST)
|
|
if (g_ActiveConfig.iLog & CONF_SAVESHADERS)
|
|
{
|
|
// save the shaders
|
|
char strfile[255];
|
|
sprintf(strfile, "%sps%.3d.txt", File::GetUserPath(D_DUMPFRAMES_IDX).c_str(), g_ActiveConfig.iSaveTargetId);
|
|
std::ofstream fps(strfile);
|
|
fps << ps->strprog.c_str();
|
|
sprintf(strfile, "%svs%.3d.txt", File::GetUserPath(D_DUMPFRAMES_IDX).c_str(), g_ActiveConfig.iSaveTargetId);
|
|
std::ofstream fvs(strfile);
|
|
fvs << vs->strprog.c_str();
|
|
}
|
|
|
|
if (g_ActiveConfig.iLog & CONF_SAVETARGETS)
|
|
{
|
|
char str[128];
|
|
sprintf(str, "%starg%.3d.tga", File::GetUserPath(D_DUMPFRAMES_IDX).c_str(), g_ActiveConfig.iSaveTargetId);
|
|
TargetRectangle tr;
|
|
tr.left = 0;
|
|
tr.right = Renderer::GetTargetWidth();
|
|
tr.top = 0;
|
|
tr.bottom = Renderer::GetTargetHeight();
|
|
g_renderer->SaveScreenshot(str, tr);
|
|
}
|
|
#endif
|
|
g_Config.iSaveTargetId++;
|
|
|
|
ClearEFBCache();
|
|
|
|
GL_REPORT_ERRORD();
|
|
}
|
|
|
|
} // namespace
|