Ryan Houdek 6bdcde9dd6 [Android] Tegra 4 'support.' This brings up the OpenGL backend to support Tegra 4 to the point where it will run games but it doesn't have any video output for some reason. This is a large change that doesn't actually change much functionally. Walking through the changes.
It changes the string in the Android backend select to just OpenGL ES.
Adds a check in the Android code to check for Tegra 4 and to enable the option to select the OpenGL ES backend.
Adds a DriverDetails bug under BUG_ISTEGRA as a blanket case of Tegra 4 support.
The changes that effects most lines in this change. Removing all float suffixes in the pixel/vertex/util shaders since OpenGL ES 2 doesn't support float suffixes.
Disables the shaders for reinterpreting the EFB format since Tegra 4 doesn't support integers.
Changes GLFunctions.cpp to grab the correct Tegra extension functions.
Readds the GLSL 1.2 'hacks' as GLSLES2 'hacks' since they are required for GLSL ES 2
Adds a GLSLES2 to the GLSL_VERSION enum.
Disable the SamplerCache on Tegra since Tegra doesn't support samplers...
Enable glBufferSubData on Tegra since it is the only mobile GPU to correctly work with it.
Disable glDrawRangeElements on Tegra since it doesn't support it, This uses glDrawElements instead.
2013-10-06 03:12:29 -05:00

254 lines
7.1 KiB
C++

// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Globals.h"
#include "GLUtil.h"
#include "StreamBuffer.h"
#include "MemoryUtil.h"
#include "Render.h"
#include "DriverDetails.h"
#include "OnScreenDisplay.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)
{
// TODO: move this to InitBackendInfo
if(g_ActiveConfig.bHackedBufferUpload && DriverDetails::HasBug(DriverDetails::BUG_BROKENHACKEDBUFFER))
{
OSD::AddMessage("Vertex Streaming Hack isn't supported by your GPU.", 10000);
g_ActiveConfig.bHackedBufferUpload = false;
g_Config.bHackedBufferUpload = false;
}
if(!g_ogl_config.bSupportsGLBaseVertex && (m_uploadtype & BUFFERDATA) && !DriverDetails::HasBug(DriverDetails::BUG_ISTEGRA))
m_uploadtype = BUFFERDATA;
else if(!g_ogl_config.bSupportsGLBaseVertex && (m_uploadtype & BUFFERSUBDATA))
m_uploadtype = BUFFERSUBDATA;
else if(g_ogl_config.bSupportsGLSync && g_ActiveConfig.bHackedBufferUpload && (m_uploadtype & MAP_AND_RISK))
m_uploadtype = MAP_AND_RISK;
else if(g_ogl_config.bSupportsGLSync && g_ogl_config.bSupportsGLPinnedMemory && (!DriverDetails::HasBug(DriverDetails::BUG_BROKENPINNEDMEMORY) || type != GL_ELEMENT_ARRAY_BUFFER) && (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:
case BUFFERDATA:
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 BUFFERDATA:
glBufferData(m_buffertype, size, data, GL_STREAM_DRAW);
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*)glMapBufferRange(m_buffertype, 0, m_size, GL_MAP_WRITE_BIT);
glUnmapBuffer(m_buffertype);
if(!pointer)
ERROR_LOG(VIDEO, "Buffer allocation failed");
break;
case BUFFERDATA:
glBindBuffer(m_buffertype, m_buffer);
break;
case STREAM_DETECT:
case DETECT_MASK: // Just to shutup warnings
break;
}
}
void StreamBuffer::Shutdown()
{
switch(m_uploadtype) {
case MAP_AND_SYNC:
DeleteFences();
break;
case MAP_AND_RISK:
case MAP_AND_ORPHAN:
case BUFFERSUBDATA:
case BUFFERDATA:
break;
case PINNED_MEMORY:
DeleteFences();
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;
}
}
void StreamBuffer::DeleteFences()
{
for(u32 i=SLOT(m_free_iterator)+1; i < SYNC_POINTS; i++)
glDeleteSync(fences[i]);
for(u32 i=0; i<SLOT(m_iterator); i++)
glDeleteSync(fences[i]);
delete [] fences;
}
}