Merge pull request #10411 from hrydgard/gl-render-manager

GL render manager - new faster GL multithreading
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2018-01-31 12:22:07 +01:00
commit 22e28e218b
83 files changed
+5242 -3626

No files matched your search

+8 -3
View File
@@ -830,7 +830,14 @@ if(ANDROID)
set(nativeExtra ${nativeExtra} ${NativeAppSource})
endif()
set(THIN3D_PLATFORMS ext/native/thin3d/thin3d_gl.cpp)
set(THIN3D_PLATFORMS ext/native/thin3d/thin3d_gl.cpp
ext/native/thin3d/GLRenderManager.cpp
ext/native/thin3d/GLRenderManager.h
ext/native/thin3d/GLQueueRunner.cpp
ext/native/thin3d/GLQueueRunner.h
ext/native/thin3d/DataFormatGL.cpp
ext/native/thin3d/DataFormatGL.h)
set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS}
ext/native/thin3d/thin3d_vulkan.cpp
ext/native/thin3d/VulkanRenderManager.cpp
@@ -887,8 +894,6 @@ add_library(native STATIC
ext/native/file/vfs.h
ext/native/file/zip_read.cpp
ext/native/file/zip_read.h
ext/native/gfx/GLStateCache.cpp
ext/native/gfx/GLStateCache.h
ext/native/gfx/gl_common.h
ext/native/gfx/gl_debug_log.cpp
ext/native/gfx/gl_debug_log.h
+8
View File
@@ -10,6 +10,9 @@ class GraphicsContext {
public:
virtual ~GraphicsContext() {}
virtual bool InitFromRenderThread(std::string *errorMessage) { return true; }
virtual void ShutdownFromRenderThread() {}
virtual void Shutdown() = 0;
virtual void SwapInterval(int interval) = 0;
@@ -25,6 +28,11 @@ public:
// Needs casting to the appropriate type, unfortunately. Should find a better solution..
virtual void *GetAPIContext() { return nullptr; }
// Called from the render thread from threaded backends.
virtual void ThreadStart() {}
virtual bool ThreadFrame() { return true; }
virtual void ThreadEnd() {}
virtual Draw::DrawContext *GetDrawContext() = 0;
};
+16 -16
View File
@@ -1090,22 +1090,22 @@ const char *VulkanResultToString(VkResult res) {
}
void VulkanDeleteList::Take(VulkanDeleteList &del) {
assert(cmdPools_.size() == 0);
assert(descPools_.size() == 0);
assert(modules_.size() == 0);
assert(buffers_.size() == 0);
assert(bufferViews_.size() == 0);
assert(images_.size() == 0);
assert(imageViews_.size() == 0);
assert(deviceMemory_.size() == 0);
assert(samplers_.size() == 0);
assert(pipelines_.size() == 0);
assert(pipelineCaches_.size() == 0);
assert(renderPasses_.size() == 0);
assert(framebuffers_.size() == 0);
assert(pipelineLayouts_.size() == 0);
assert(descSetLayouts_.size() == 0);
assert(callbacks_.size() == 0);
assert(cmdPools_.empty());
assert(descPools_.empty());
assert(modules_.empty());
assert(buffers_.empty());
assert(bufferViews_.empty());
assert(images_.empty());
assert(imageViews_.empty());
assert(deviceMemory_.empty());
assert(samplers_.empty());
assert(pipelines_.empty());
assert(pipelineCaches_.empty());
assert(renderPasses_.empty());
assert(framebuffers_.empty());
assert(pipelineLayouts_.empty());
assert(descSetLayouts_.empty());
assert(callbacks_.empty());
cmdPools_ = std::move(del.cmdPools_);
descPools_ = std::move(del.descPools_);
modules_ = std::move(del.modules_);
+2 -3
View File
@@ -75,9 +75,8 @@ bool VulkanPushBuffer::AddBuffer() {
return false;
}
buf_ = buffers_.size();
buffers_.resize(buf_ + 1);
buffers_[buf_] = info;
buffers_.push_back(info);
buf_ = buffers_.size() - 1;
return true;
}
+4
View File
@@ -24,11 +24,15 @@ class GraphicsContext;
// called from emu thread
void UpdateRunLoop();
void Core_Run(GraphicsContext *ctx);
void Core_Stop();
void Core_ErrorPause();
// For platforms that don't call Core_Run
void Core_SetGraphicsContext(GraphicsContext *ctx);
void Core_RunRenderThreadFrame();
// called from gui
void Core_EnableStepping(bool step);
void Core_DoSingleStep();
+4 -168
View File
@@ -81,12 +81,7 @@ ParamSFOData g_paramSFO;
static GlobalUIState globalUIState;
static CoreParameter coreParameter;
static FileLoader *loadedFile;
static std::thread *cpuThread = nullptr;
static std::thread::id cpuThreadID;
static std::mutex cpuThreadLock;
static std::condition_variable cpuThreadCond;
static std::condition_variable cpuThreadReplyCond;
static u64 cpuThreadUntil;
bool audioInitialized;
bool coreCollectDebugStats = false;
@@ -142,45 +137,6 @@ void Audio_Shutdown() {
}
}
bool IsOnSeparateCPUThread() {
if (cpuThread != nullptr) {
return cpuThreadID == std::this_thread::get_id();
} else {
return false;
}
}
void CPU_SetStateNoLock(CPUThreadState to) {
cpuThreadState = to;
cpuThreadCond.notify_one();
cpuThreadReplyCond.notify_one();
}
void CPU_SetState(CPUThreadState to) {
std::lock_guard<std::mutex> guard(cpuThreadLock);
CPU_SetStateNoLock(to);
}
bool CPU_NextState(CPUThreadState from, CPUThreadState to) {
std::lock_guard<std::mutex> guard(cpuThreadLock);
if (cpuThreadState == from) {
CPU_SetStateNoLock(to);
return true;
} else {
return false;
}
}
bool CPU_NextStateNot(CPUThreadState from, CPUThreadState to) {
std::lock_guard<std::mutex> guard(cpuThreadLock);
if (cpuThreadState != from) {
CPU_SetStateNoLock(to);
return true;
} else {
return false;
}
}
bool CPU_IsReady() {
if (coreState == CORE_POWERUP)
return false;
@@ -195,13 +151,6 @@ bool CPU_HasPendingAction() {
return cpuThreadState != CPU_THREAD_RUNNING;
}
void CPU_WaitStatus(std::condition_variable &cond, bool (*pred)()) {
std::unique_lock<std::mutex> guard(cpuThreadLock);
while (!pred()) {
cond.wait(guard);
}
}
void CPU_Shutdown();
void CPU_Init() {
@@ -278,7 +227,6 @@ void CPU_Init() {
if (!LoadFile(&loadedFile, &coreParameter.errorString)) {
CPU_Shutdown();
coreParameter.fileToStart = "";
CPU_SetState(CPU_THREAD_NOT_RUNNING);
return;
}
@@ -321,52 +269,6 @@ void UpdateLoadedFile(FileLoader *fileLoader) {
loadedFile = fileLoader;
}
void CPU_RunLoop() {
setCurrentThreadName("CPU");
if (CPU_NextState(CPU_THREAD_PENDING, CPU_THREAD_STARTING)) {
CPU_Init();
CPU_NextState(CPU_THREAD_STARTING, CPU_THREAD_RUNNING);
} else if (!CPU_NextState(CPU_THREAD_RESUME, CPU_THREAD_RUNNING)) {
ERROR_LOG(CPU, "CPU thread in unexpected state: %d", cpuThreadState);
return;
}
while (cpuThreadState != CPU_THREAD_SHUTDOWN)
{
CPU_WaitStatus(cpuThreadCond, &CPU_HasPendingAction);
switch (cpuThreadState) {
case CPU_THREAD_EXECUTE:
mipsr4k.RunLoopUntil(cpuThreadUntil);
CPU_NextState(CPU_THREAD_EXECUTE, CPU_THREAD_RUNNING);
break;
// These are fine, just keep looping.
case CPU_THREAD_RUNNING:
case CPU_THREAD_SHUTDOWN:
break;
case CPU_THREAD_QUIT:
// Just leave the thread, CPU is switching off thread.
CPU_SetState(CPU_THREAD_NOT_RUNNING);
return;
default:
ERROR_LOG(CPU, "CPU thread in unexpected state: %d", cpuThreadState);
// Begin shutdown, otherwise we'd just spin on this bad state.
CPU_SetState(CPU_THREAD_SHUTDOWN);
break;
}
}
if (coreState != CORE_ERROR) {
coreState = CORE_POWERDOWN;
}
CPU_Shutdown();
CPU_SetState(CPU_THREAD_NOT_RUNNING);
}
void Core_UpdateState(CoreState newState) {
if ((coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME) && newState != CORE_RUNNING)
coreStatePending = true;
@@ -384,11 +286,6 @@ void Core_UpdateDebugStats(bool collectStats) {
gpuStats.ResetFrame();
}
void System_Wake() {
// Ping the threads so they check coreState.
CPU_NextStateNot(CPU_THREAD_NOT_RUNNING, CPU_THREAD_SHUTDOWN);
}
// Ugly!
static bool pspIsInited = false;
static bool pspIsIniting = false;
@@ -415,17 +312,7 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) {
coreParameter.errorString = "";
pspIsIniting = true;
// Keeping this around because we might need it in the future.
const bool separateCPUThread = false;
if (separateCPUThread) {
Core_ListenShutdown(System_Wake);
CPU_SetState(CPU_THREAD_PENDING);
cpuThread = new std::thread(&CPU_RunLoop);
cpuThreadID = cpuThread->get_id();
cpuThread->detach();
} else {
CPU_Init();
}
CPU_Init();
*error_string = coreParameter.errorString;
bool success = coreParameter.fileToStart != "";
@@ -461,11 +348,6 @@ bool PSP_InitUpdate(std::string *error_string) {
bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) {
PSP_InitStart(coreParam, error_string);
// For a potential resurrection of separate CPU thread later.
if (false) {
CPU_WaitStatus(cpuThreadReplyCond, &CPU_IsReady);
}
while (!PSP_InitUpdate(error_string))
sleep_ms(10);
return pspIsInited;
@@ -496,15 +378,7 @@ void PSP_Shutdown() {
if (coreState == CORE_RUNNING)
Core_UpdateState(CORE_ERROR);
Core_NotifyShutdown();
if (cpuThread != nullptr) {
CPU_NextStateNot(CPU_THREAD_NOT_RUNNING, CPU_THREAD_SHUTDOWN);
CPU_WaitStatus(cpuThreadReplyCond, &CPU_IsShutdown);
delete cpuThread;
cpuThread = 0;
cpuThreadID = std::thread::id();
} else {
CPU_Shutdown();
}
CPU_Shutdown();
GPU_Shutdown();
g_paramSFO.Clear();
host->SetWindowTitle(0);
@@ -534,45 +408,7 @@ void PSP_RunLoopUntil(u64 globalticks) {
return;
}
// We no longer allow a separate CPU thread but if we add a render queue
// to GL we're gonna need it.
bool useCPUThread = false;
if (useCPUThread && cpuThread == nullptr) {
// Need to start the cpu thread.
Core_ListenShutdown(System_Wake);
CPU_SetState(CPU_THREAD_RESUME);
cpuThread = new std::thread(&CPU_RunLoop);
cpuThreadID = cpuThread->get_id();
cpuThread->detach();
// Probably needs to tell the gpu that it will need to queue up its output
// on another thread.
CPU_WaitStatus(cpuThreadReplyCond, &CPU_IsReady);
} else if (!useCPUThread && cpuThread != nullptr) {
CPU_SetState(CPU_THREAD_QUIT);
CPU_WaitStatus(cpuThreadReplyCond, &CPU_IsShutdown);
delete cpuThread;
cpuThread = nullptr;
cpuThreadID = std::thread::id();
}
if (cpuThread != nullptr) {
cpuThreadUntil = globalticks;
if (CPU_NextState(CPU_THREAD_RUNNING, CPU_THREAD_EXECUTE)) {
// The CPU doesn't actually respect cpuThreadUntil well, especially when skipping frames.
// TODO: Something smarter? Or force CPU to bail periodically?
while (!CPU_IsReady()) {
// Have the GPU do stuff here.
if (coreState != CORE_RUNNING) {
CPU_WaitStatus(cpuThreadReplyCond, &CPU_IsReady);
}
}
} else {
ERROR_LOG(CPU, "Unable to execute CPU run loop, unexpected state: %d", cpuThreadState);
}
} else {
mipsr4k.RunLoopUntil(globalticks);
}
mipsr4k.RunLoopUntil(globalticks);
gpu->CleanupBeforeUI();
}
-2
View File
@@ -78,8 +78,6 @@ void Core_UpdateDebugStats(bool collectStats);
void Audio_Init();
void Audio_Shutdown();
bool IsOnSeparateCPUThread();
bool IsAudioInitialised();
void UpdateLoadedFile(FileLoader *fileLoader);
+1
View File
@@ -170,6 +170,7 @@ protected:
colStride = 4;
}
virtual void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) = 0;
virtual void EndFrame() {}
};
TessellationDataTransfer *tessDataTransfer;
};
+19 -12
View File
@@ -208,6 +208,7 @@ void FramebufferManagerCommon::SetNumExtraFBOs(int num) {
extraFBOs_.push_back(fbo);
}
currentRenderVfb_ = 0;
// TODO: Should probably not do this bind.
if (num != 0)
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
}
@@ -608,6 +609,8 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe
// performance-crushing framebuffer reloads from RAM, but we'll have to live with that.
if (vfb->last_frame_render != gpuStats.numFlips) {
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
// GLES resets the blend state on clears.
gstate_c.Dirty(DIRTY_BLEND_STATE);
} else {
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
}
@@ -718,8 +721,6 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
float u0 = 0.0f, u1 = 1.0f;
float v0 = 0.0f, v1 = 1.0f;
MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height, u1, v1);
if (useBufferedRendering_ && vfb && vfb->fbo) {
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
SetViewport2D(0, 0, vfb->renderWidth, vfb->renderHeight);
@@ -735,6 +736,8 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
}
DisableState();
MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height, u1, v1);
DrawTextureFlags flags = (vfb || g_Config.iBufFilter == SCALE_LINEAR) ? DRAWTEX_LINEAR : DRAWTEX_NEAREST;
Bind2DShader();
DrawActiveTexture(dstX, dstY, width, height, vfb->bufferWidth, vfb->bufferHeight, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags);
@@ -851,6 +854,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
if (useBufferedRendering_) {
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
}
gstate_c.Dirty(DIRTY_BLEND_STATE);
return;
}
@@ -920,6 +924,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
SetViewport2D(0, 0, pixelWidth_, pixelHeight_);
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
DrawFramebufferToOutput(Memory::GetPointer(displayFramebufPtr_), displayFormat_, displayStride_, true);
gstate_c.Dirty(DIRTY_BLEND_STATE);
return;
}
} else {
@@ -929,6 +934,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
// Bind and clear the backbuffer. This should be the first time during the frame that it's bound.
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
}
gstate_c.Dirty(DIRTY_BLEND_STATE);
return;
}
}
@@ -1031,13 +1037,6 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
SetViewport2D(0, 0, pixelWidth_, pixelHeight_);
DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, uvRotation, flags);
}
/*
if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
draw_->BindFramebufferAsRenderTarget(extraFBOs_[0], { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT };
glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, attachments);
}*/
} else {
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
draw_->BindFramebufferAsTexture(vfb->fbo, 0, Draw::FB_COLOR_BIT, 0);
@@ -1070,6 +1069,9 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
else if (useBufferedRendering_) {
WARN_LOG(FRAMEBUF, "Current VFB lacks an FBO: %08x", vfb->fb_address);
}
// This may get called mid-draw if the game uses an immediate flip.
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE);
}
void FramebufferManagerCommon::DecimateFBOs() {
@@ -1191,6 +1193,8 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w,
INFO_LOG(FRAMEBUF, "Resizing FBO for %08x : %d x %d x %d", vfb->fb_address, w, h, vfb->format);
if (vfb->fbo) {
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
// GLES resets the blend state on clears.
gstate_c.Dirty(DIRTY_BLEND_STATE);
if (!skipCopy && !g_Config.bDisableSlowFramebufEffects) {
BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0);
}
@@ -1201,6 +1205,8 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w,
}
} else {
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
// GLES resets the blend state on clears.
gstate_c.Dirty(DIRTY_BLEND_STATE);
}
if (!vfb->fbo) {
@@ -1936,9 +1942,8 @@ bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEB
buffer.Allocate(w, h, GE_FORMAT_8888, flipY, true);
bool retval = draw_->CopyFramebufferToMemorySync(bound, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w);
gpuStats.numReadbacks++;
// Vulkan requires us to re-apply all dynamic state for each command buffer, and the above will cause us to start a new cmdbuf.
// So let's dirty the things that are involved in Vulkan dynamic state. Readbacks are not frequent so this won't hurt other backends.
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE);
// After a readback we'll have flushed and started over, need to dirty a bunch of things to be safe.
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
// We may have blitted to a temp FBO.
RebindFramebuffer();
return retval;
@@ -1972,6 +1977,8 @@ bool FramebufferManagerCommon::GetDepthbuffer(u32 fb_address, int fb_stride, u32
}
// No need to free on failure, that's the caller's job (it likely will reuse a buffer.)
bool retval = draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, 0, w, h, Draw::DataFormat::D32F, buffer.GetData(), w);
// After a readback we'll have flushed and started over, need to dirty a bunch of things to be safe.
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
// That may have unbound the framebuffer, rebind to avoid crashes when debugging.
RebindFramebuffer();
return retval;
+3 -3
View File
@@ -206,7 +206,7 @@ public:
return vfb;
}
}
virtual void RebindFramebuffer();
void RebindFramebuffer();
std::vector<FramebufferInfo> GetFramebufferList();
void CopyDisplayToOutput();
@@ -222,9 +222,9 @@ public:
bool NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason);
void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason);
virtual void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h);
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h);
virtual void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes);
void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes);
void DrawFramebufferToOutput(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, bool applyPostShader);
void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height);
+5 -11
View File
@@ -576,17 +576,11 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo
// This is a bit of a hack as the render buffer isn't always that size
// We always scissor on non-buffered so that clears don't spill outside the frame.
if (useBufferedRendering && scissorX1 == 0 && scissorY1 == 0
&& scissorX2 >= (int)gstate_c.curRTWidth
&& scissorY2 >= (int)gstate_c.curRTHeight) {
out.scissorEnable = false;
} else {
out.scissorEnable = true;
out.scissorX = renderX + displayOffsetX + scissorX1 * renderWidthFactor;
out.scissorY = renderY + displayOffsetY + scissorY1 * renderHeightFactor;
out.scissorW = (scissorX2 - scissorX1) * renderWidthFactor;
out.scissorH = (scissorY2 - scissorY1) * renderHeightFactor;
}
out.scissorEnable = true;
out.scissorX = renderX + displayOffsetX + scissorX1 * renderWidthFactor;
out.scissorY = renderY + displayOffsetY + scissorY1 * renderHeightFactor;
out.scissorW = (scissorX2 - scissorX1) * renderWidthFactor;
out.scissorH = (scissorY2 - scissorY1) * renderHeightFactor;
int curRTWidth = gstate_c.curRTWidth;
int curRTHeight = gstate_c.curRTHeight;
+1 -1
View File
@@ -383,7 +383,7 @@ void TextureCacheCommon::SetTexture(bool force) {
// Exponential backoff up to 512 frames. Textures are often reused.
if (entry->numFrames > 32) {
// Also, try to add some "randomness" to avoid rehashing several textures the same frame.
entry->framesUntilNextFullHash = std::min(512, entry->numFrames) + (entry->textureName & 15);
entry->framesUntilNextFullHash = std::min(512, entry->numFrames) + (((intptr_t)(entry->textureName) >> 12) & 15);
} else {
entry->framesUntilNextFullHash = entry->numFrames;
}
+3 -9
View File
@@ -87,6 +87,8 @@ struct SamplerCacheKey {
}
};
class GLRTexture;
// TODO: Shrink this struct. There is some fluff.
struct TexCacheEntry {
~TexCacheEntry() {
@@ -132,7 +134,7 @@ struct TexCacheEntry {
u16 dim;
u16 bufw;
union {
u32 textureName;
GLRTexture *textureName;
void *texturePtr;
CachedTextureVulkan *vkTex;
};
@@ -146,16 +148,8 @@ struct TexCacheEntry {
u32 framesUntilNextFullHash;
u32 fullhash;
u32 cluthash;
float lodBias;
u16 maxSeenV;
// Cache the current filter settings so we can avoid setting it again.
// (OpenGL ES 2.0 madness where filter settings are attached to each texture. Unused in other backends).
u8 magFilt;
u8 minFilt;
bool sClamp;
bool tClamp;
TexStatus GetHashStatus() {
return TexStatus(status & STATUS_MASK);
}
-9
View File
@@ -459,15 +459,6 @@ void FramebufferManagerD3D11::BindPostShader(const PostShaderUniforms &uniforms)
context_->PSSetConstantBuffers(0, 1, &postConstants_);
}
void FramebufferManagerD3D11::RebindFramebuffer() {
if (currentRenderVfb_ && currentRenderVfb_->fbo) {
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
} else {
// Should this even happen?
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
}
}
void FramebufferManagerD3D11::ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old) {
if (!useBufferedRendering_ || !vfb->fbo) {
return;
-2
View File
@@ -59,8 +59,6 @@ public:
virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override;
virtual void RebindFramebuffer() override;
// TODO: Remove
ID3D11Buffer *GetDynamicQuadBuffer() {
return quadBuffer_;
+4
View File
@@ -462,4 +462,8 @@ void DrawEngineD3D11::ApplyDrawStateLate(bool applyStencilRef, uint8_t stencilRe
context_->OMSetDepthStencilState(depthStencilState_, applyStencilRef ? stencilRef : dynState_.stencilRef);
}
gstate_c.Clean(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_BLEND_STATE);
// Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects.
if (fboTexBound_)
gstate_c.Dirty(DIRTY_BLEND_STATE);
}
+8 -6
View File
@@ -775,16 +775,18 @@ bool TextureCacheD3D11::DecodeTexture(u8 *output, const GPUgstate &state) {
}
bool TextureCacheD3D11::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) {
ApplyTexture();
SetTexture(false);
if (!nextTexture_)
return false;
// Apply texture may need to rebuild the texture if we're about to render, or bind a framebuffer.
TexCacheEntry *entry = nextTexture_;
ApplyTexture();
// TODO: Centralize.
if (nextTexture_->framebuffer) {
VirtualFramebuffer *vfb = nextTexture_->framebuffer;
bool flipY = GetGPUBackend() == GPUBackend::OPENGL && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE;
buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, flipY);
if (entry->framebuffer) {
VirtualFramebuffer *vfb = entry->framebuffer;
buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, false);
bool retval = draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, 0, vfb->bufferWidth, vfb->bufferHeight, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), vfb->bufferWidth);
// Vulkan requires us to re-apply all dynamic state for each command buffer, and the above will cause us to start a new cmdbuf.
// So let's dirty the things that are involved in Vulkan dynamic state. Readbacks are not frequent so this won't hurt other backends.
@@ -794,7 +796,7 @@ bool TextureCacheD3D11::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level
return retval;
}
ID3D11Texture2D *texture = (ID3D11Texture2D *)nextTexture_->texturePtr;
ID3D11Texture2D *texture = (ID3D11Texture2D *)entry->texturePtr;
if (!texture)
return false;
-1
View File
@@ -171,7 +171,6 @@ void TextureCacheDX9::UpdateSamplingParams(TexCacheEntry &entry, bool force) {
dxstate.texMaxMipLevel.set(0);
dxstate.texMipLodBias.set(0.0f);
}
entry.lodBias = lodBias;
} else {
dxstate.texMaxMipLevel.set(0);
dxstate.texMipLodBias.set(0.0f);
+39 -115
View File
@@ -23,7 +23,6 @@
#include "Core/Reporting.h"
#include "DepalettizeShaderGLES.h"
#include "GPU/GLES/TextureCacheGLES.h"
#include "ext/native/gfx/GLStateCache.h"
#include "GPU/Common/DepalettizeShaderCommon.h"
#ifdef _WIN32
@@ -58,38 +57,8 @@ static const char *depalVShader300 =
" gl_Position = a_position;\n"
"}\n";
static bool CheckShaderCompileSuccess(GLuint shader, const char *code) {
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
#define MAX_INFO_LOG_SIZE 2048
GLchar infoLog[MAX_INFO_LOG_SIZE];
GLsizei len;
glGetShaderInfoLog(shader, MAX_INFO_LOG_SIZE, &len, infoLog);
infoLog[len] = '\0';
#ifdef __ANDROID__
ELOG("Error in shader compilation! %s\n", infoLog);
ELOG("Shader source:\n%s\n", (const char *)code);
#endif
ERROR_LOG(G3D, "Error in shader compilation!\n");
ERROR_LOG(G3D, "Info log: %s\n", infoLog);
ERROR_LOG(G3D, "Shader source:\n%s\n", (const char *)code);
#ifdef SHADERLOG
OutputDebugStringUTF8(infoLog);
#endif
shader = 0;
return false;
} else {
DEBUG_LOG(G3D, "Compiled shader:\n%s\n", (const char *)code);
#ifdef SHADERLOG
OutputDebugStringUTF8(code);
#endif
return true;
}
}
DepalShaderCacheGLES::DepalShaderCacheGLES() {
DepalShaderCacheGLES::DepalShaderCacheGLES(Draw::DrawContext *draw) {
render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
// Pre-build the vertex program
useGL3_ = gl_extensions.GLES3 || gl_extensions.VersionGEThan(3, 3);
@@ -102,25 +71,12 @@ DepalShaderCacheGLES::~DepalShaderCacheGLES() {
}
bool DepalShaderCacheGLES::CreateVertexShader() {
if (vertexShaderFailed_) {
return false;
}
vertexShader_ = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader_, 1, useGL3_ ? &depalVShader300 : &depalVShader100, 0);
glCompileShader(vertexShader_);
if (!CheckShaderCompileSuccess(vertexShader_, useGL3_ ? depalVShader300 : depalVShader100)) {
glDeleteShader(vertexShader_);
vertexShader_ = 0;
// Don't try to recompile.
vertexShaderFailed_ = true;
}
return !vertexShaderFailed_;
std::string src(useGL3_ ? depalVShader300 : depalVShader100);
vertexShader_ = render_->CreateShader(GL_VERTEX_SHADER, src, "depal");
return true;
}
GLuint DepalShaderCacheGLES::GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut) {
GLRTexture *DepalShaderCacheGLES::GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut) {
u32 clutId = GetClutID(clutFormat, clutHash);
auto oldtex = texCache_.find(clutId);
@@ -133,18 +89,13 @@ GLuint DepalShaderCacheGLES::GetClutTexture(GEPaletteFormat clutFormat, const u3
int texturePixels = clutFormat == GE_CMODE_32BIT_ABGR8888 ? 256 : 512;
DepalTexture *tex = new DepalTexture();
glGenTextures(1, &tex->texture);
glBindTexture(GL_TEXTURE_2D, tex->texture);
tex->texture = render_->CreateTexture(GL_TEXTURE_2D);
GLuint components = dstFmt == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA;
GLuint components2 = components;
glTexImage2D(GL_TEXTURE_2D, 0, components, texturePixels, 1, 0, components2, dstFmt, (void *)rawClut);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
uint8_t *clutCopy = new uint8_t[1024];
memcpy(clutCopy, rawClut, 1024);
render_->TextureImage(tex->texture, 0, texturePixels, 1, components, components2, dstFmt, clutCopy, false);
tex->lastFrame = gpuStats.numFlips;
texCache_[clutId] = tex;
@@ -153,20 +104,20 @@ GLuint DepalShaderCacheGLES::GetClutTexture(GEPaletteFormat clutFormat, const u3
void DepalShaderCacheGLES::Clear() {
for (auto shader = cache_.begin(); shader != cache_.end(); ++shader) {
glDeleteShader(shader->second->fragShader);
render_->DeleteShader(shader->second->fragShader);
if (shader->second->program) {
glDeleteProgram(shader->second->program);
render_->DeleteProgram(shader->second->program);
}
delete shader->second;
}
cache_.clear();
for (auto tex = texCache_.begin(); tex != texCache_.end(); ++tex) {
glDeleteTextures(1, &tex->second->texture);
render_->DeleteTexture(tex->second->texture);
delete tex->second;
}
texCache_.clear();
if (vertexShader_) {
glDeleteShader(vertexShader_);
render_->DeleteShader(vertexShader_);
vertexShader_ = 0;
}
}
@@ -174,7 +125,7 @@ void DepalShaderCacheGLES::Clear() {
void DepalShaderCacheGLES::Decimate() {
for (auto tex = texCache_.begin(); tex != texCache_.end(); ) {
if (tex->second->lastFrame + DEPAL_TEXTURE_OLD_AGE < gpuStats.numFlips) {
glDeleteTextures(1, &tex->second->texture);
render_->DeleteTexture(tex->second->texture);
delete tex->second;
texCache_.erase(tex++);
} else {
@@ -188,10 +139,14 @@ DepalShader *DepalShaderCacheGLES::GetDepalettizeShader(uint32_t clutMode, GEBuf
auto shader = cache_.find(id);
if (shader != cache_.end()) {
DepalShader *depal = shader->second;
// If compile failed previously, try to recover.
if (depal->fragShader->failed || vertexShader_->failed)
return nullptr;
return shader->second;
}
if (vertexShader_ == 0) {
if (!vertexShader_) {
if (!CreateVertexShader()) {
// The vertex shader failed, no need to bother trying the fragment.
return nullptr;
@@ -201,64 +156,33 @@ DepalShader *DepalShaderCacheGLES::GetDepalettizeShader(uint32_t clutMode, GEBuf
char *buffer = new char[2048];
GenerateDepalShader(buffer, pixelFormat, useGL3_ ? GLSL_300 : GLSL_140);
GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);
const char *buf = buffer;
glShaderSource(fragShader, 1, &buf, 0);
glCompileShader(fragShader);
CheckShaderCompileSuccess(fragShader, buffer);
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader_);
glAttachShader(program, fragShader);
glBindAttribLocation(program, 0, "a_position");
glBindAttribLocation(program, 1, "a_texcoord0");
glLinkProgram(program);
glUseProgram(program);
GLint u_tex = glGetUniformLocation(program, "tex");
GLint u_pal = glGetUniformLocation(program, "pal");
glUniform1i(u_tex, 0);
glUniform1i(u_pal, 3);
std::string src(buffer);
GLRShader *fragShader = render_->CreateShader(GL_FRAGMENT_SHADER, src, "depal");
DepalShader *depal = new DepalShader();
std::vector<GLRProgram::Semantic> semantics;
semantics.push_back({ 0, "a_position" });
semantics.push_back({ 1, "a_texcoord0" });
std::vector<GLRProgram::UniformLocQuery> queries;
queries.push_back({ &depal->u_tex, "tex" });
queries.push_back({ &depal->u_pal, "pal" });
std::vector<GLRProgram::Initializer> initializer;
initializer.push_back({ &depal->u_tex, 0, 0 });
initializer.push_back({ &depal->u_pal, 0, 3 });
std::vector<GLRShader *> shaders{ vertexShader_, fragShader };
GLRProgram *program = render_->CreateProgram(shaders, semantics, queries, initializer, false);
depal->program = program;
depal->fragShader = fragShader;
depal->code = buffer;
cache_[id] = depal;
GLint linkStatus = GL_FALSE;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus != GL_TRUE) {
GLint bufLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
if (bufLength) {
char* errorbuf = new char[bufLength];
glGetProgramInfoLog(program, bufLength, NULL, errorbuf);
#ifdef SHADERLOG
OutputDebugStringUTF8(buffer);
OutputDebugStringUTF8(errorbuf);
#endif
ERROR_LOG(G3D, "Could not link program:\n %s \n\n %s", errorbuf, buf);
delete[] errorbuf; // we're dead!
}
// Since it failed, let's mark it in the cache so we don't keep retrying.
// That will only make it slower.
depal->program = 0;
// We will delete the shader later in Clear().
glDeleteProgram(program);
} else {
depal->a_position = glGetAttribLocation(program, "a_position");
depal->a_texcoord0 = glGetAttribLocation(program, "a_texcoord0");
}
delete[] buffer;
return depal->program ? depal : nullptr;
}
+11 -8
View File
@@ -19,34 +19,36 @@
#include "Common/CommonTypes.h"
#include "gfx/gl_common.h"
#include "thin3d/thin3d.h"
#include "thin3d/GLRenderManager.h"
#include "GPU/ge_constants.h"
#include "GPU/Common/ShaderCommon.h"
#include "GPU/Common/DepalettizeShaderCommon.h"
class DepalShader {
public:
GLuint program;
GLuint fragShader;
GLint a_position;
GLint a_texcoord0;
GLRProgram *program;
GLRShader *fragShader;
GLint u_tex;
GLint u_pal;
std::string code;
};
class DepalTexture {
public:
GLuint texture;
GLRTexture *texture;
int lastFrame;
};
// Caches both shaders and palette textures.
class DepalShaderCacheGLES : public DepalShaderCacheCommon {
public:
DepalShaderCacheGLES();
DepalShaderCacheGLES(Draw::DrawContext *draw);
~DepalShaderCacheGLES();
// This also uploads the palette and binds the correct texture.
DepalShader *GetDepalettizeShader(uint32_t clutMode, GEBufferFormat pixelFormat);
GLuint GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut);
GLRTexture *GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut);
void Clear();
void Decimate();
std::vector<std::string> DebugGetShaderIDs(DebugShaderType type);
@@ -55,9 +57,10 @@ public:
private:
bool CreateVertexShader();
GLRenderManager *render_;
bool useGL3_;
bool vertexShaderFailed_;
GLuint vertexShader_;
GLRShader *vertexShader_;
std::map<u32, DepalShader *> cache_;
std::map<u32, DepalTexture *> texCache_;
};
+204 -386
View File
@@ -83,7 +83,6 @@
#include "GPU/Common/SplineCommon.h"
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SoftwareTransformCommon.h"
#include "ext/native/gfx/GLStateCache.h"
#include "GPU/GLES/FragmentTestCacheGLES.h"
#include "GPU/GLES/StateMappingGLES.h"
#include "GPU/GLES/TextureCacheGLES.h"
@@ -91,7 +90,7 @@
#include "GPU/GLES/ShaderManagerGLES.h"
#include "GPU/GLES/GPU_GLES.h"
extern const GLuint glprim[8] = {
const GLuint glprim[8] = {
GL_POINTS,
GL_LINES,
GL_LINE_STRIP,
@@ -115,7 +114,8 @@ enum {
enum { VAI_KILL_AGE = 120, VAI_UNRELIABLE_KILL_AGE = 240, VAI_UNRELIABLE_KILL_MAX = 4 };
DrawEngineGLES::DrawEngineGLES() : vai_(256) {
DrawEngineGLES::DrawEngineGLES(Draw::DrawContext *draw) : vai_(256), draw_(draw), inputLayoutMap_(16) {
render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
decOptions_.expandAllWeightsToFloat = false;
decOptions_.expand8BitNormalsToFloat = false;
@@ -133,7 +133,7 @@ DrawEngineGLES::DrawEngineGLES() : vai_(256) {
InitDeviceObjects();
tessDataTransfer = new TessellationDataTransferGLES(gl_extensions.VersionGEThan(3, 0, 0));
tessDataTransfer = new TessellationDataTransferGLES(render_);
}
DrawEngineGLES::~DrawEngineGLES() {
@@ -145,17 +145,6 @@ DrawEngineGLES::~DrawEngineGLES() {
delete tessDataTransfer;
}
void DrawEngineGLES::RestoreVAO() {
if (sharedVao_ != 0) {
glBindVertexArray(sharedVao_);
} else if (gstate_c.Supports(GPU_SUPPORTS_VAO)) {
// Note: this is here because, InitDeviceObjects() is called before GPU_SUPPORTS_VAO is setup.
// So, this establishes it if Supports() returns true and there isn't one yet.
glGenVertexArrays(1, &sharedVao_);
glBindVertexArray(sharedVao_);
}
}
void DrawEngineGLES::DeviceLost() {
DestroyDeviceObjects();
}
@@ -165,35 +154,64 @@ void DrawEngineGLES::DeviceRestore() {
}
void DrawEngineGLES::InitDeviceObjects() {
if (bufferNameCache_.empty()) {
bufferNameCache_.resize(VERTEXCACHE_NAME_CACHE_SIZE);
glGenBuffers(VERTEXCACHE_NAME_CACHE_SIZE, &bufferNameCache_[0]);
bufferNameCacheSize_ = 0;
for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) {
frameData_[i].pushVertex = new GLPushBuffer(render_, GL_ARRAY_BUFFER, 1024 * 1024);
frameData_[i].pushIndex = new GLPushBuffer(render_, GL_ELEMENT_ARRAY_BUFFER, 256 * 1024);
if (gstate_c.Supports(GPU_SUPPORTS_VAO)) {
glGenVertexArrays(1, &sharedVao_);
} else {
sharedVao_ = 0;
}
} else {
ERROR_LOG(G3D, "Device objects already initialized!");
render_->RegisterPushBuffer(i, frameData_[i].pushVertex);
render_->RegisterPushBuffer(i, frameData_[i].pushIndex);
}
int vertexSize = sizeof(TransformedVertex);
std::vector<GLRInputLayout::Entry> entries;
entries.push_back({ ATTR_POSITION, 4, GL_FLOAT, GL_FALSE, vertexSize, 0 });
entries.push_back({ ATTR_TEXCOORD, 3, GL_FLOAT, GL_FALSE, vertexSize, offsetof(TransformedVertex, u) });
entries.push_back({ ATTR_COLOR0, 4, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, offsetof(TransformedVertex, color0) });
entries.push_back({ ATTR_COLOR1, 3, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, offsetof(TransformedVertex, color1) });
softwareInputLayout_ = render_->CreateInputLayout(entries);
}
void DrawEngineGLES::DestroyDeviceObjects() {
ClearTrackedVertexArrays();
if (!bufferNameCache_.empty()) {
glstate.arrayBuffer.unbind();
glstate.elementArrayBuffer.unbind();
glDeleteBuffers((GLsizei)bufferNameCache_.size(), &bufferNameCache_[0]);
bufferNameCache_.clear();
bufferNameInfo_.clear();
freeSizedBuffers_.clear();
bufferNameCacheSize_ = 0;
if (sharedVao_ != 0) {
glDeleteVertexArrays(1, &sharedVao_);
}
// Beware: this could be called twice in a row, sometimes.
for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) {
if (!frameData_[i].pushVertex && !frameData_[i].pushIndex)
continue;
render_->UnregisterPushBuffer(i, frameData_[i].pushVertex);
render_->UnregisterPushBuffer(i, frameData_[i].pushIndex);
frameData_[i].pushVertex->Destroy();
frameData_[i].pushIndex->Destroy();
delete frameData_[i].pushVertex;
delete frameData_[i].pushIndex;
frameData_[i].pushVertex = nullptr;
frameData_[i].pushIndex = nullptr;
}
ClearTrackedVertexArrays();
if (softwareInputLayout_)
render_->DeleteInputLayout(softwareInputLayout_);
softwareInputLayout_ = nullptr;
}
void DrawEngineGLES::ClearInputLayoutMap() {
inputLayoutMap_.Iterate([&](const uint32_t &key, GLRInputLayout *il) {
render_->DeleteInputLayout(il);
});
inputLayoutMap_.Clear();
}
void DrawEngineGLES::BeginFrame() {
FrameData &frameData = frameData_[render_->GetCurFrame()];
frameData.pushIndex->Begin();
frameData.pushVertex->Begin();
}
void DrawEngineGLES::EndFrame() {
FrameData &frameData = frameData_[render_->GetCurFrame()];
frameData.pushIndex->End();
frameData.pushVertex->End();
tessDataTransfer->EndFrame();
}
struct GlTypeInfo {
@@ -220,24 +238,40 @@ static const GlTypeInfo GLComp[] = {
{GL_UNSIGNED_SHORT, 4, GL_TRUE},// DEC_U16_4,
};
static inline void VertexAttribSetup(int attrib, int fmt, int stride, u8 *ptr) {
static inline void VertexAttribSetup(int attrib, int fmt, int stride, int offset, std::vector<GLRInputLayout::Entry> &entries) {
if (fmt) {
const GlTypeInfo &type = GLComp[fmt];
glVertexAttribPointer(attrib, type.count, type.type, type.normalized, stride, ptr);
GLRInputLayout::Entry entry;
entry.offset = offset;
entry.location = attrib;
entry.normalized = type.normalized;
entry.type = type.type;
entry.stride = stride;
entry.count = type.count;
entries.push_back(entry);
}
}
// TODO: Use VBO and get rid of the vertexData pointers - with that, we will supply only offsets
static void SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt, u8 *vertexData) {
CHECK_GL_ERROR_IF_DEBUG();
VertexAttribSetup(ATTR_W1, decFmt.w0fmt, decFmt.stride, vertexData + decFmt.w0off);
VertexAttribSetup(ATTR_W2, decFmt.w1fmt, decFmt.stride, vertexData + decFmt.w1off);
VertexAttribSetup(ATTR_TEXCOORD, decFmt.uvfmt, decFmt.stride, vertexData + decFmt.uvoff);
VertexAttribSetup(ATTR_COLOR0, decFmt.c0fmt, decFmt.stride, vertexData + decFmt.c0off);
VertexAttribSetup(ATTR_COLOR1, decFmt.c1fmt, decFmt.stride, vertexData + decFmt.c1off);
VertexAttribSetup(ATTR_NORMAL, decFmt.nrmfmt, decFmt.stride, vertexData + decFmt.nrmoff);
VertexAttribSetup(ATTR_POSITION, decFmt.posfmt, decFmt.stride, vertexData + decFmt.posoff);
CHECK_GL_ERROR_IF_DEBUG();
GLRInputLayout *DrawEngineGLES::SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt) {
uint32_t key = decFmt.id;
GLRInputLayout *inputLayout = inputLayoutMap_.Get(key);
if (inputLayout) {
return inputLayout;
}
std::vector<GLRInputLayout::Entry> entries;
VertexAttribSetup(ATTR_W1, decFmt.w0fmt, decFmt.stride, decFmt.w0off, entries);
VertexAttribSetup(ATTR_W2, decFmt.w1fmt, decFmt.stride, decFmt.w1off, entries);
VertexAttribSetup(ATTR_TEXCOORD, decFmt.uvfmt, decFmt.stride, decFmt.uvoff, entries);
VertexAttribSetup(ATTR_COLOR0, decFmt.c0fmt, decFmt.stride, decFmt.c0off, entries);
VertexAttribSetup(ATTR_COLOR1, decFmt.c1fmt, decFmt.stride, decFmt.c1off, entries);
VertexAttribSetup(ATTR_NORMAL, decFmt.nrmfmt, decFmt.stride, decFmt.nrmoff, entries);
VertexAttribSetup(ATTR_POSITION, decFmt.posfmt, decFmt.stride, decFmt.posoff, entries);
inputLayout = render_->CreateInputLayout(entries);
inputLayoutMap_.Insert(key, inputLayout);
return inputLayout;
}
void DrawEngineGLES::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) {
@@ -304,14 +338,25 @@ void DrawEngineGLES::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, i
}
}
void DrawEngineGLES::DecodeVertsToPushBuffer(GLPushBuffer *push, uint32_t *bindOffset, GLRBuffer **buf) {
u8 *dest = decoded;
// Figure out how much pushbuffer space we need to allocate.
if (push) {
int vertsToDecode = ComputeNumVertsToDecode();
dest = (u8 *)push->Push(vertsToDecode * dec_->GetDecVtxFmt().stride, bindOffset, buf);
}
DecodeVerts(dest);
}
void DrawEngineGLES::MarkUnreliable(VertexArrayInfo *vai) {
vai->status = VertexArrayInfo::VAI_UNRELIABLE;
if (vai->vbo) {
FreeBuffer(vai->vbo);
render_->DeleteBuffer(vai->vbo);
vai->vbo = 0;
}
if (vai->ebo) {
FreeBuffer(vai->ebo);
render_->DeleteBuffer(vai->ebo);
vai->ebo = 0;
}
}
@@ -351,99 +396,43 @@ void DrawEngineGLES::DecimateTrackedVertexArrays() {
vai_.Maintain();
}
GLuint DrawEngineGLES::AllocateBuffer(size_t sz) {
GLuint unused = 0;
auto freeMatch = freeSizedBuffers_.find(sz);
if (freeMatch != freeSizedBuffers_.end()) {
unused = freeMatch->second;
_assert_(!bufferNameInfo_[unused].used);
freeSizedBuffers_.erase(freeMatch);
} else {
for (GLuint buf : bufferNameCache_) {
const BufferNameInfo &info = bufferNameInfo_[buf];
if (info.used) {
continue;
}
// Just pick the first unused one, we'll have to resize it.
unused = buf;
// Let's also remove from the free list, if it's there.
if (info.sz != 0) {
auto range = freeSizedBuffers_.equal_range(info.sz);
for (auto it = range.first; it != range.second; ++it) {
if (it->second == buf) {
// It will only be once, so remove and bail.
freeSizedBuffers_.erase(it);
break;
}
}
}
break;
}
}
if (unused == 0) {
size_t oldSize = bufferNameCache_.size();
bufferNameCache_.resize(oldSize + VERTEXCACHE_NAME_CACHE_SIZE);
glGenBuffers(VERTEXCACHE_NAME_CACHE_SIZE, &bufferNameCache_[oldSize]);
unused = bufferNameCache_[oldSize];
}
BufferNameInfo &info = bufferNameInfo_[unused];
// Record the change in size.
bufferNameCacheSize_ += sz - info.sz;
info.sz = sz;
info.used = true;
return unused;
}
void DrawEngineGLES::FreeBuffer(GLuint buf) {
// We can reuse buffers by setting new data on them, so let's actually keep it.
auto it = bufferNameInfo_.find(buf);
if (it != bufferNameInfo_.end()) {
it->second.used = false;
it->second.lastFrame = gpuStats.numFlips;
if (it->second.sz != 0) {
freeSizedBuffers_.insert(std::make_pair(it->second.sz, buf));
}
} else {
ERROR_LOG(G3D, "Unexpected buffer freed (%d) but not tracked", buf);
}
}
void DrawEngineGLES::FreeVertexArray(VertexArrayInfo *vai) {
if (vai->vbo) {
FreeBuffer(vai->vbo);
vai->vbo = 0;
render_->DeleteBuffer(vai->vbo);
vai->vbo = nullptr;
}
if (vai->ebo) {
FreeBuffer(vai->ebo);
vai->ebo = 0;
render_->DeleteBuffer(vai->ebo);
vai->ebo = nullptr;
}
}
void DrawEngineGLES::DoFlush() {
PROFILE_THIS_SCOPE("flush");
CHECK_GL_ERROR_IF_DEBUG();
FrameData &frameData = frameData_[render_->GetCurFrame()];
gpuStats.numFlushes++;
gpuStats.numTrackedVertexArrays = (int)vai_.size();
bool textureNeedsApply = false;
if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) {
textureCache_->SetTexture();
gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
textureNeedsApply = true;
}
GEPrimitiveType prim = prevPrim_;
ApplyDrawState(prim);
CHECK_GL_ERROR_IF_DEBUG();
VShaderID vsid;
Shader *vshader = shaderManager_->ApplyVertexShader(prim, lastVType_, &vsid);
GLRBuffer *vertexBuffer = nullptr;
GLRBuffer *indexBuffer = nullptr;
uint32_t vertexBufferOffset = 0;
uint32_t indexBufferOffset = 0;
if (vshader->UseHWTransform()) {
GLuint vbo = 0, ebo = 0;
int vertexCount = 0;
bool useElements = true;
@@ -453,6 +442,9 @@ void DrawEngineGLES::DoFlush() {
if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK))
useCache = false;
// TEMPORARY
useCache = false;
if (useCache) {
u32 id = dcid_ ^ gstate.getUVGenMode(); // This can have an effect on which UV decoder we need to use! And hence what the decoded data will look like. See #9263
VertexArrayInfo *vai = vai_.Get(id);
@@ -534,31 +526,27 @@ void DrawEngineGLES::DoFlush() {
_dbg_assert_msg_(G3D, gstate_c.vertBounds.minV >= gstate_c.vertBounds.maxV, "Should not have checked UVs when caching.");
size_t vsz = dec_->GetDecVtxFmt().stride * indexGen.MaxIndex();
vai->vbo = AllocateBuffer(vsz);
glstate.arrayBuffer.bind(vai->vbo);
glBufferData(GL_ARRAY_BUFFER, vsz, decoded, GL_STATIC_DRAW);
vai->vbo = render_->CreateBuffer(GL_ARRAY_BUFFER, vsz, GL_STATIC_DRAW);
render_->BufferSubdata(vai->vbo, 0, vsz, decoded);
// If there's only been one primitive type, and it's either TRIANGLES, LINES or POINTS,
// there is no need for the index buffer we built. We can then use glDrawArrays instead
// for a very minor speed boost.
if (useElements) {
size_t esz = sizeof(short) * indexGen.VertexCount();
vai->ebo = AllocateBuffer(esz);
glstate.elementArrayBuffer.bind(vai->ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, esz, (GLvoid *)decIndex, GL_STATIC_DRAW);
vai->ebo = render_->CreateBuffer(GL_ARRAY_BUFFER, esz, GL_STATIC_DRAW);
render_->BufferSubdata(vai->ebo, 0, esz, (uint8_t *)decIndex, false);
} else {
vai->ebo = 0;
glstate.elementArrayBuffer.bind(vai->ebo);
render_->BindIndexBuffer(vai->ebo);
}
} else {
gpuStats.numCachedDrawCalls++;
glstate.arrayBuffer.bind(vai->vbo);
glstate.elementArrayBuffer.bind(vai->ebo);
useElements = vai->ebo ? true : false;
gpuStats.numCachedVertsDrawn += vai->numVerts;
gstate_c.vertexFullAlpha = vai->flags & VAI_FLAG_VERTEXFULLALPHA;
}
vbo = vai->vbo;
ebo = vai->ebo;
vertexBuffer = vai->vbo;
indexBuffer = vai->ebo;
vertexCount = vai->numVerts;
prim = static_cast<GEPrimitiveType>(vai->prim);
break;
@@ -573,10 +561,8 @@ void DrawEngineGLES::DoFlush() {
}
gpuStats.numCachedDrawCalls++;
gpuStats.numCachedVertsDrawn += vai->numVerts;
vbo = vai->vbo;
ebo = vai->ebo;
glstate.arrayBuffer.bind(vbo);
glstate.elementArrayBuffer.bind(ebo);
vertexBuffer = vai->vbo;
indexBuffer = vai->ebo;
vertexCount = vai->numVerts;
prim = static_cast<GEPrimitiveType>(vai->prim);
@@ -597,7 +583,15 @@ void DrawEngineGLES::DoFlush() {
vai->lastFrame = gpuStats.numFlips;
} else {
DecodeVerts(decoded);
if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK)) {
// If software skinning, we've already predecoded into "decoded". So push that content.
size_t size = decodedVerts_ * dec_->GetDecVtxFmt().stride;
u8 *dest = (u8 *)frameData.pushVertex->Push(size, &vertexBufferOffset, &vertexBuffer);
memcpy(dest, decoded, size);
} else {
// Decode directly into the pushbuffer
DecodeVertsToPushBuffer(frameData.pushVertex, &vertexBufferOffset, &vertexBuffer);
}
rotateVBO:
gpuStats.numUncachedVertsDrawn += indexGen.VertexCount();
@@ -606,9 +600,6 @@ rotateVBO:
if (!useElements && indexGen.PureCount()) {
vertexCount = indexGen.PureCount();
}
glstate.arrayBuffer.unbind();
glstate.elementArrayBuffer.unbind();
prim = indexGen.Prim();
}
@@ -620,26 +611,27 @@ rotateVBO:
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255);
}
ApplyDrawStateLate();
if (gstate_c.Supports(GPU_SUPPORTS_VAO) && vbo == 0) {
vbo = BindBuffer(decoded, dec_->GetDecVtxFmt().stride * indexGen.MaxIndex());
if (useElements) {
ebo = BindElementBuffer(decIndex, sizeof(short) * indexGen.VertexCount());
}
}
if (textureNeedsApply)
textureCache_->ApplyTexture();
// Need to ApplyDrawState after ApplyTexture because depal can launch a render pass and that wrecks the state.
ApplyDrawState(prim);
ApplyDrawStateLate(false, 0);
LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, prim);
SetupDecFmtForDraw(program, dec_->GetDecVtxFmt(), vbo ? 0 : decoded);
GLRInputLayout *inputLayout = SetupDecFmtForDraw(program, dec_->GetDecVtxFmt());
render_->BindVertexBuffer(inputLayout, vertexBuffer, vertexBufferOffset);
if (useElements) {
if (!indexBuffer) {
indexBufferOffset = (uint32_t)frameData.pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &indexBuffer);
render_->BindIndexBuffer(indexBuffer);
}
if (gstate_c.bezier || gstate_c.spline)
// Instanced rendering for instanced tessellation
glDrawElementsInstanced(glprim[prim], vertexCount, GL_UNSIGNED_SHORT, ebo ? 0 : (GLvoid*)decIndex, numPatches);
render_->DrawIndexed(glprim[prim], vertexCount, GL_UNSIGNED_SHORT, (GLvoid*)(intptr_t)indexBufferOffset, numPatches);
else
glDrawElements(glprim[prim], vertexCount, GL_UNSIGNED_SHORT, ebo ? 0 : (GLvoid*)decIndex);
render_->DrawIndexed(glprim[prim], vertexCount, GL_UNSIGNED_SHORT, (GLvoid*)(intptr_t)indexBufferOffset);
} else {
glDrawArrays(glprim[prim], 0, vertexCount);
render_->Draw(glprim[prim], 0, vertexCount);
}
} else {
DecodeVerts(decoded);
@@ -683,40 +675,30 @@ rotateVBO:
prim, vertexCount,
dec_->VertexType(), inds, GE_VTYPE_IDX_16BIT, dec_->GetDecVtxFmt(),
maxIndex, drawBuffer, numTrans, drawIndexed, &params, &result);
ApplyDrawStateLate();
if (textureNeedsApply)
textureCache_->ApplyTexture();
ApplyDrawState(prim);
ApplyDrawStateLate(result.setStencil, result.stencilValue);
LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, prim);
if (result.action == SW_DRAW_PRIMITIVES) {
if (result.setStencil) {
glstate.stencilFunc.set(GL_ALWAYS, result.stencilValue, 255);
}
const int vertexSize = sizeof(transformed[0]);
bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX;
const uint8_t *bufferStart = (const uint8_t *)drawBuffer;
if (gstate_c.Supports(GPU_SUPPORTS_VAO)) {
bufferStart = 0;
BindBuffer(drawBuffer, vertexSize * maxIndex);
if (drawIndexed) {
BindElementBuffer(inds, sizeof(short) * numTrans);
inds = 0;
}
} else {
glstate.arrayBuffer.unbind();
glstate.elementArrayBuffer.unbind();
}
glVertexAttribPointer(ATTR_POSITION, 4, GL_FLOAT, GL_FALSE, vertexSize, bufferStart);
int attrMask = program->attrMask;
if (attrMask & (1 << ATTR_TEXCOORD)) glVertexAttribPointer(ATTR_TEXCOORD, doTextureProjection ? 3 : 2, GL_FLOAT, GL_FALSE, vertexSize, bufferStart + offsetof(TransformedVertex, u));
if (attrMask & (1 << ATTR_COLOR0)) glVertexAttribPointer(ATTR_COLOR0, 4, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, bufferStart + offsetof(TransformedVertex, color0));
if (attrMask & (1 << ATTR_COLOR1)) glVertexAttribPointer(ATTR_COLOR1, 3, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, bufferStart + offsetof(TransformedVertex, color1));
if (drawIndexed) {
glDrawElements(glprim[prim], numTrans, GL_UNSIGNED_SHORT, inds);
vertexBufferOffset = (uint32_t)frameData.pushVertex->Push(drawBuffer, maxIndex * sizeof(TransformedVertex), &vertexBuffer);
indexBufferOffset = (uint32_t)frameData.pushIndex->Push(inds, sizeof(uint16_t) * numTrans, &indexBuffer);
render_->BindVertexBuffer(softwareInputLayout_, vertexBuffer, vertexBufferOffset);
render_->BindIndexBuffer(indexBuffer);
render_->DrawIndexed(glprim[prim], numTrans, GL_UNSIGNED_SHORT, (void *)(intptr_t)indexBufferOffset);
} else {
glDrawArrays(glprim[prim], 0, numTrans);
vertexBufferOffset = (uint32_t)frameData.pushVertex->Push(drawBuffer, numTrans * sizeof(TransformedVertex), &vertexBuffer);
render_->BindVertexBuffer(softwareInputLayout_, vertexBuffer, vertexBufferOffset);
render_->Draw(glprim[prim], 0, numTrans);
}
} else if (result.action == SW_CLEAR) {
u32 clearColor = result.color;
@@ -735,26 +717,14 @@ rotateVBO:
framebufferManager_->SetDepthUpdated();
}
// Note that scissor may still apply while clearing. Turn off other tests for the clear.
glstate.stencilTest.disable();
glstate.stencilMask.set(0xFF);
glstate.depthTest.disable();
GLbitfield target = 0;
// Without this, we will clear RGB when clearing stencil, which breaks games.
uint8_t rgbaMask = (colorMask ? 7 : 0) | (alphaMask ? 8 : 0);
if (colorMask || alphaMask) target |= GL_COLOR_BUFFER_BIT;
if (alphaMask) target |= GL_STENCIL_BUFFER_BIT;
if (depthMask) target |= GL_DEPTH_BUFFER_BIT;
glstate.colorMask.set(colorMask, colorMask, colorMask, alphaMask);
glClearColor(col[0], col[1], col[2], col[3]);
#ifdef USING_GLES2
glClearDepthf(clearDepth);
#else
glClearDepth(clearDepth);
#endif
// Stencil takes alpha.
glClearStencil(clearColor >> 24);
glClear(target);
render_->Clear(clearColor, clearDepth, clearColor >> 24, target, rgbaMask);
framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason);
int scissorX1 = gstate.getScissorX1();
@@ -766,6 +736,7 @@ rotateVBO:
if (g_Config.bBlockTransferGPU && (gstate_c.featureFlags & GPU_USE_CLEAR_RAM_HACK) && colorMask && (alphaMask || gstate.FrameBufFormat() == GE_FORMAT_565)) {
framebufferManager_->ApplyClearToMemory(scissorX1, scissorY1, scissorX2, scissorY2, clearColor);
}
gstate_c.Dirty(DIRTY_BLEND_STATE); // Make sure the color mask gets re-applied.
}
}
@@ -791,104 +762,6 @@ rotateVBO:
#ifndef MOBILE_DEVICE
host->GPUNotifyDraw();
#endif
CHECK_GL_ERROR_IF_DEBUG();
}
GLuint DrawEngineGLES::BindBuffer(const void *p, size_t sz) {
// Get a new buffer each time we need one.
GLuint buf = AllocateBuffer(sz);
glstate.arrayBuffer.bind(buf);
// These aren't used more than once per frame, so let's use GL_STREAM_DRAW.
glBufferData(GL_ARRAY_BUFFER, sz, p, GL_STREAM_DRAW);
buffersThisFrame_.push_back(buf);
return buf;
}
GLuint DrawEngineGLES::BindBuffer(const void *p1, size_t sz1, const void *p2, size_t sz2) {
GLuint buf = AllocateBuffer(sz1 + sz2);
glstate.arrayBuffer.bind(buf);
glBufferData(GL_ARRAY_BUFFER, sz1 + sz2, nullptr, GL_STREAM_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sz1, p1);
glBufferSubData(GL_ARRAY_BUFFER, sz1, sz2, p2);
buffersThisFrame_.push_back(buf);
return buf;
}
GLuint DrawEngineGLES::BindElementBuffer(const void *p, size_t sz) {
GLuint buf = AllocateBuffer(sz);
glstate.elementArrayBuffer.bind(buf);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sz, p, GL_STREAM_DRAW);
buffersThisFrame_.push_back(buf);
return buf;
}
void DrawEngineGLES::DecimateBuffers() {
for (GLuint buf : buffersThisFrame_) {
FreeBuffer(buf);
}
buffersThisFrame_.clear();
if (--bufferDecimationCounter_ <= 0) {
bufferDecimationCounter_ = VERTEXCACHE_DECIMATION_INTERVAL;
} else {
return;
}
// Let's not keep too many around, will eat up memory.
// First check if there's any to free, and only check if it seems somewhat full.
bool hasOld = false;
if (bufferNameCacheSize_ > VERTEXCACHE_NAME_CACHE_FULL_BYTES) {
for (GLuint buf : bufferNameCache_) {
const BufferNameInfo &info = bufferNameInfo_[buf];
const int age = gpuStats.numFlips - info.lastFrame;
if (!info.used && age > VERTEXCACHE_NAME_CACHE_MAX_AGE) {
hasOld = true;
break;
}
}
}
if (hasOld) {
// Okay, it is. Let's rebuild the array.
std::vector<GLuint> toFree;
std::vector<GLuint> toKeep;
toKeep.reserve(bufferNameCache_.size());
for (size_t i = 0, n = bufferNameCache_.size(); i < n; ++i) {
const GLuint buf = bufferNameCache_[i];
const BufferNameInfo &info = bufferNameInfo_[buf];
const int age = gpuStats.numFlips - info.lastFrame;
if (!info.used && age > VERTEXCACHE_NAME_CACHE_MAX_AGE) {
toFree.push_back(buf);
bufferNameCacheSize_ -= bufferNameInfo_[buf].sz;
bufferNameInfo_.erase(buf);
// If we've removed all we want to this round, keep the rest and abort.
if (toFree.size() >= VERTEXCACHE_NAME_DECIMATION_MAX && i + 1 < bufferNameCache_.size()) {
toKeep.insert(toKeep.end(), bufferNameCache_.begin() + i + 1, bufferNameCache_.end());
break;
}
} else {
toKeep.push_back(buf);
}
}
if (!toFree.empty()) {
bufferNameCache_ = toKeep;
// TODO: Rebuild?
freeSizedBuffers_.clear();
glstate.arrayBuffer.unbind();
glstate.elementArrayBuffer.unbind();
glDeleteBuffers((GLsizei)toFree.size(), &toFree[0]);
}
}
}
bool DrawEngineGLES::IsCodePtrVertexDecoder(const u8 *ptr) const {
@@ -896,100 +769,45 @@ bool DrawEngineGLES::IsCodePtrVertexDecoder(const u8 *ptr) const {
}
void DrawEngineGLES::TessellationDataTransferGLES::SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) {
#ifndef USING_GLES2
if (isAllowTexture1D_) {
// Position
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_1D, data_tex[0]);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (prevSize < size) {
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, size, 0, GL_RGBA, GL_FLOAT, (GLfloat*)pos);
prevSize = size;
} else {
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, size, GL_RGBA, GL_FLOAT, (GLfloat*)pos);
}
// Removed the 1D texture support, it's unlikely to be relevant for performance.
if (data_tex[0])
renderManager_->DeleteTexture(data_tex[0]);
uint8_t *pos_data = new uint8_t[size * sizeof(float) * 4];
memcpy(pos_data, pos, size * sizeof(float) * 4);
data_tex[0] = renderManager_->CreateTexture(GL_TEXTURE_2D);
renderManager_->TextureImage(data_tex[0], 0, size, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT, pos_data, false);
renderManager_->FinalizeTexture(data_tex[0], 0, false);
renderManager_->BindTexture(4, data_tex[0]);
// Texcoords
if (hasTexCoords) {
glActiveTexture(GL_TEXTURE5);
glBindTexture(GL_TEXTURE_1D, data_tex[1]);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (prevSizeTex < size) {
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, size, 0, GL_RGBA, GL_FLOAT, (GLfloat*)tex);
prevSizeTex = size;
} else {
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, size, GL_RGBA, GL_FLOAT, (GLfloat*)tex);
}
}
// Texcoords
if (hasTexCoords) {
if (data_tex[1])
renderManager_->DeleteTexture(data_tex[1]);
uint8_t *tex_data = new uint8_t[size * sizeof(float) * 4];
memcpy(tex_data, pos, size * sizeof(float) * 4);
data_tex[1] = renderManager_->CreateTexture(GL_TEXTURE_2D);
renderManager_->TextureImage(data_tex[1], 0, size, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT, tex_data, false);
renderManager_->FinalizeTexture(data_tex[1], 0, false);
renderManager_->BindTexture(5, data_tex[1]);
}
// Color
glActiveTexture(GL_TEXTURE6);
glBindTexture(GL_TEXTURE_1D, data_tex[2]);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
int sizeColor = hasColor ? size : 1;
if (prevSizeCol < sizeColor) {
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, sizeColor, 0, GL_RGBA, GL_FLOAT, (GLfloat*)col);
prevSizeCol = sizeColor;
} else {
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, sizeColor, GL_RGBA, GL_FLOAT, (GLfloat*)col);
}
} else
#endif
{
// Position
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, data_tex[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (prevSize < size) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, size, 1, 0, GL_RGBA, GL_FLOAT, (GLfloat*)pos);
prevSize = size;
} else {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, 1, GL_RGBA, GL_FLOAT, (GLfloat*)pos);
}
if (data_tex[2])
renderManager_->DeleteTexture(data_tex[2]);
data_tex[2] = renderManager_->CreateTexture(GL_TEXTURE_2D);
int sizeColor = hasColor ? size : 1;
uint8_t *col_data = new uint8_t[sizeColor * sizeof(float) * 4];
memcpy(col_data, col, sizeColor * sizeof(float) * 4);
// Texcoords
if (hasTexCoords) {
glActiveTexture(GL_TEXTURE5);
glBindTexture(GL_TEXTURE_2D, data_tex[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (prevSizeTex < size) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, size, 1, 0, GL_RGBA, GL_FLOAT, (GLfloat*)tex);
prevSizeTex = size;
} else {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, 1, GL_RGBA, GL_FLOAT, (GLfloat*)tex);
}
}
renderManager_->TextureImage(data_tex[2], 0, sizeColor, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT, col_data, false);
renderManager_->FinalizeTexture(data_tex[2], 0, false);
renderManager_->BindTexture(6, data_tex[2]);
}
// Color
glActiveTexture(GL_TEXTURE6);
glBindTexture(GL_TEXTURE_2D, data_tex[2]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
int sizeColor = hasColor ? size : 1;
if (prevSizeCol < sizeColor) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, sizeColor, 1, 0, GL_RGBA, GL_FLOAT, (GLfloat*)col);
prevSizeCol = sizeColor;
} else {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sizeColor, 1, GL_RGBA, GL_FLOAT, (GLfloat*)col);
void DrawEngineGLES::TessellationDataTransferGLES::EndFrame() {
for (int i = 0; i < 3; i++) {
if (data_tex[i]) {
renderManager_->DeleteTexture(data_tex[i]);
data_tex[i] = nullptr;
}
}
glActiveTexture(GL_TEXTURE0);
CHECK_GL_ERROR_IF_DEBUG();
}
+40 -34
View File
@@ -28,6 +28,7 @@
#include "GPU/Common/GPUStateUtils.h"
#include "GPU/GLES/FragmentShaderGeneratorGLES.h"
#include "gfx/gl_common.h"
#include "thin3d/GLRenderManager.h"
class LinkedShader;
class ShaderManagerGLES;
@@ -60,8 +61,8 @@ class VertexArrayInfo {
public:
VertexArrayInfo() {
status = VAI_NEW;
vbo = 0;
ebo = 0;
vbo = nullptr;
ebo = nullptr;
prim = GE_PRIM_INVALID;
numDraws = 0;
numFrames = 0;
@@ -81,8 +82,8 @@ public:
ReliableHashType hash;
u32 minihash;
u32 vbo;
u32 ebo;
GLRBuffer *vbo;
GLRBuffer *ebo;
// Precalculated parameter for drawRangeElements
u16 numVerts;
@@ -101,7 +102,7 @@ public:
// Handles transform, lighting and drawing.
class DrawEngineGLES : public DrawEngineCommon {
public:
DrawEngineGLES();
DrawEngineGLES(Draw::DrawContext *draw);
virtual ~DrawEngineGLES();
void SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead);
@@ -118,7 +119,6 @@ public:
void SetFragmentTestCache(FragmentTestCacheGLES *testCache) {
fragmentTestCache_ = testCache;
}
void RestoreVAO();
void DeviceLost();
void DeviceRestore();
@@ -126,6 +126,10 @@ public:
void ClearTrackedVertexArrays() override;
void DecimateTrackedVertexArrays();
void BeginFrame();
void EndFrame();
// So that this can be inlined
void Flush() {
if (!numDrawCalls)
@@ -136,7 +140,7 @@ public:
void FinishDeferred() {
if (!numDrawCalls)
return;
DecodeVerts(decoded);
DoFlush();
}
bool IsCodePtrVertexDecoder(const u8 *ptr) const;
@@ -146,10 +150,14 @@ public:
SubmitPrim(verts, inds, prim, vertexCount, vertType, bytesRead);
}
GLuint BindBuffer(const void *p, size_t sz);
GLuint BindBuffer(const void *p1, size_t sz1, const void *p2, size_t sz2);
GLuint BindElementBuffer(const void *p, size_t sz);
void DecimateBuffers();
GLPushBuffer *GetPushVertexBuffer() {
return frameData_[render_->GetCurFrame()].pushVertex;
}
GLPushBuffer *GetPushIndexBuffer() {
return frameData_[render_->GetCurFrame()].pushIndex;
}
void ClearInputLayoutMap();
private:
void InitDeviceObjects();
@@ -157,53 +165,51 @@ private:
void DoFlush();
void ApplyDrawState(int prim);
void ApplyDrawStateLate();
void ApplyDrawStateLate(bool setStencil, int stencilValue);
void ResetShaderBlending();
GLuint AllocateBuffer(size_t sz);
void FreeBuffer(GLuint buf);
GLRInputLayout *SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt);
void DecodeVertsToPushBuffer(GLPushBuffer *push, uint32_t *bindOffset, GLRBuffer **buf);
void FreeVertexArray(VertexArrayInfo *vai);
void MarkUnreliable(VertexArrayInfo *vai);
struct FrameData {
GLPushBuffer *pushVertex;
GLPushBuffer *pushIndex;
};
FrameData frameData_[GLRenderManager::MAX_INFLIGHT_FRAMES];
PrehashMap<VertexArrayInfo *, nullptr> vai_;
// Vertex buffer objects
// Element buffer objects
struct BufferNameInfo {
BufferNameInfo() : sz(0), used(false), lastFrame(0) {}
DenseHashMap<uint32_t, GLRInputLayout *, nullptr> inputLayoutMap_;
size_t sz;
bool used;
int lastFrame;
};
std::vector<GLuint> bufferNameCache_;
std::multimap<size_t, GLuint> freeSizedBuffers_;
std::unordered_map<GLuint, BufferNameInfo> bufferNameInfo_;
std::vector<GLuint> buffersThisFrame_;
size_t bufferNameCacheSize_ = 0;
GLuint sharedVao_ = 0;
GLRInputLayout *softwareInputLayout_ = nullptr;
GLRenderManager *render_;
// Other
ShaderManagerGLES *shaderManager_ = nullptr;
TextureCacheGLES *textureCache_ = nullptr;
FramebufferManagerGLES *framebufferManager_ = nullptr;
FragmentTestCacheGLES *fragmentTestCache_ = nullptr;
Draw::DrawContext *draw_;
int bufferDecimationCounter_ = 0;
// Hardware tessellation
class TessellationDataTransferGLES : public TessellationDataTransfer {
private:
int data_tex[3];
bool isAllowTexture1D_;
GLRTexture *data_tex[3]{};
GLRenderManager *renderManager_;
public:
TessellationDataTransferGLES(bool isAllowTexture1D) : TessellationDataTransfer(), data_tex(), isAllowTexture1D_(isAllowTexture1D) {
glGenTextures(3, (GLuint*)data_tex);
}
TessellationDataTransferGLES(GLRenderManager *renderManager)
: renderManager_(renderManager) { }
~TessellationDataTransferGLES() {
glDeleteTextures(3, (GLuint*)data_tex);
EndFrame();
}
void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) override;
void EndFrame() override; // Queues textures for deletion.
};
};
+15 -30
View File
@@ -15,6 +15,7 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "thin3d/thin3d.h"
#include "gfx/gl_debug_log.h"
#include "Core/Config.h"
#include "GPU/GLES/FragmentTestCacheGLES.h"
@@ -26,16 +27,15 @@
static const int FRAGTEST_TEXTURE_OLD_AGE = 307;
static const int FRAGTEST_DECIMATION_INTERVAL = 113;
FragmentTestCacheGLES::FragmentTestCacheGLES() : textureCache_(NULL), lastTexture_(0), decimationCounter_(0) {
scratchpad_ = new u8[256 * 4];
FragmentTestCacheGLES::FragmentTestCacheGLES(Draw::DrawContext *draw) {
render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
}
FragmentTestCacheGLES::~FragmentTestCacheGLES() {
Clear();
delete [] scratchpad_;
}
void FragmentTestCacheGLES::BindTestTexture(GLenum unit) {
void FragmentTestCacheGLES::BindTestTexture(int slot) {
if (!g_Config.bFragmentTestCache) {
return;
}
@@ -51,17 +51,12 @@ void FragmentTestCacheGLES::BindTestTexture(GLenum unit) {
const auto cached = cache_.find(id);
if (cached != cache_.end()) {
cached->second.lastFrame = gpuStats.numFlips;
GLuint tex = cached->second.texture;
GLRTexture *tex = cached->second.texture;
if (tex == lastTexture_) {
// Already bound, hurray.
return;
}
CHECK_GL_ERROR_IF_DEBUG();
glActiveTexture(unit);
glBindTexture(GL_TEXTURE_2D, tex);
// Always return to the default.
glActiveTexture(GL_TEXTURE0);
CHECK_GL_ERROR_IF_DEBUG();
render_->BindTexture(slot, tex);
lastTexture_ = tex;
return;
}
@@ -79,13 +74,9 @@ void FragmentTestCacheGLES::BindTestTexture(GLenum unit) {
const GEComparison funcs[4] = {gstate.getColorTestFunction(), gstate.getColorTestFunction(), gstate.getColorTestFunction(), gstate.getAlphaTestFunction()};
const bool valid[4] = {gstate.isColorTestEnabled(), gstate.isColorTestEnabled(), gstate.isColorTestEnabled(), gstate.isAlphaTestEnabled()};
glActiveTexture(unit);
// This will necessarily bind the texture.
const GLuint tex = CreateTestTexture(funcs, refs, masks, valid);
// Always return to the default.
glActiveTexture(GL_TEXTURE0);
GLRTexture *tex = CreateTestTexture(funcs, refs, masks, valid);
lastTexture_ = tex;
render_->BindTexture(slot, tex);
FragmentTestTexture item;
item.lastFrame = gpuStats.numFlips;
item.texture = tex;
@@ -106,7 +97,8 @@ FragmentTestID FragmentTestCacheGLES::GenerateTestID() const {
return id;
}
GLuint FragmentTestCacheGLES::CreateTestTexture(const GEComparison funcs[4], const u8 refs[4], const u8 masks[4], const bool valid[4]) {
GLRTexture *FragmentTestCacheGLES::CreateTestTexture(const GEComparison funcs[4], const u8 refs[4], const u8 masks[4], const bool valid[4]) {
u8 *data = new u8[256 * 4];
// TODO: Might it be better to use GL_ALPHA for simple textures?
// TODO: Experiment with 4-bit/etc. textures.
@@ -142,26 +134,19 @@ GLuint FragmentTestCacheGLES::CreateTestTexture(const GEComparison funcs[4], con
break;
}
}
scratchpad_[color * 4 + i] = res ? 0xFF : 0;
data[color * 4 + i] = res ? 0xFF : 0;
}
}
GLuint tex = textureCache_->AllocTextureName();
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, scratchpad_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLRTexture *tex = render_->CreateTexture(GL_TEXTURE_2D);
render_->TextureImage(tex, 0, 256, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, data);
return tex;
}
void FragmentTestCacheGLES::Clear(bool deleteThem) {
if (deleteThem) {
for (auto tex = cache_.begin(); tex != cache_.end(); ++tex) {
glDeleteTextures(1, &tex->second.texture);
render_->DeleteTexture(tex->second.texture);
}
}
cache_.clear();
@@ -172,7 +157,7 @@ void FragmentTestCacheGLES::Decimate() {
if (--decimationCounter_ <= 0) {
for (auto tex = cache_.begin(); tex != cache_.end(); ) {
if (tex->second.lastFrame + FRAGTEST_TEXTURE_OLD_AGE < gpuStats.numFlips) {
glDeleteTextures(1, &tex->second.texture);
render_->DeleteTexture(tex->second.texture);
cache_.erase(tex++);
} else {
++tex;
+7 -8
View File
@@ -19,7 +19,6 @@
#include <map>
#include "Common/CommonTypes.h"
#include "ext/native/gfx/GLStateCache.h"
#include "GPU/GLES/TextureCacheGLES.h"
#include "GPU/ge_constants.h"
@@ -53,33 +52,33 @@ struct FragmentTestID {
};
struct FragmentTestTexture {
GLuint texture;
GLRTexture *texture;
int lastFrame;
};
class FragmentTestCacheGLES {
public:
FragmentTestCacheGLES();
FragmentTestCacheGLES(Draw::DrawContext *draw);
~FragmentTestCacheGLES();
void SetTextureCache(TextureCacheGLES *tc) {
textureCache_ = tc;
}
void BindTestTexture(GLenum unit);
void BindTestTexture(int slot);
void Clear(bool deleteThem = true);
void Decimate();
private:
GLuint CreateTestTexture(const GEComparison funcs[4], const u8 refs[4], const u8 masks[4], const bool valid[4]);
GLRTexture *CreateTestTexture(const GEComparison funcs[4], const u8 refs[4], const u8 masks[4], const bool valid[4]);
FragmentTestID GenerateTestID() const;
GLRenderManager *render_;
TextureCacheGLES *textureCache_;
std::map<FragmentTestID, FragmentTestTexture> cache_;
u8 *scratchpad_;
GLuint lastTexture_;
int decimationCounter_;
GLRTexture *lastTexture_ = nullptr;
int decimationCounter_ = 0;
};
+138 -456
View File
@@ -42,7 +42,6 @@
#include "GPU/Common/TextureDecoder.h"
#include "GPU/Common/FramebufferCommon.h"
#include "GPU/Debugger/Stepping.h"
#include "ext/native/gfx/GLStateCache.h"
#include "GPU/GLES/FramebufferManagerGLES.h"
#include "GPU/GLES/TextureCacheGLES.h"
#include "GPU/GLES/DrawEngineGLES.h"
@@ -82,17 +81,6 @@ const int MAX_PBO = 2;
void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format);
void FramebufferManagerGLES::DisableState() {
glstate.blend.disable();
glstate.cullFace.disable();
glstate.depthTest.disable();
glstate.scissorTest.disable();
glstate.stencilTest.disable();
#if !defined(USING_GLES2)
glstate.colorLogicOp.disable();
#endif
glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glstate.stencilMask.set(0xFF);
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
}
@@ -102,13 +90,20 @@ void FramebufferManagerGLES::CompileDraw2DProgram() {
static std::string vs_code, fs_code;
vs_code = ApplyGLSLPrelude(basic_vs, GL_VERTEX_SHADER);
fs_code = ApplyGLSLPrelude(tex_fs, GL_FRAGMENT_SHADER);
draw2dprogram_ = glsl_create_source(vs_code.c_str(), fs_code.c_str(), &errorString);
if (!draw2dprogram_) {
ERROR_LOG_REPORT(G3D, "Failed to compile draw2dprogram! This shouldn't happen.\n%s", errorString.c_str());
} else {
glsl_bind(draw2dprogram_);
glUniform1i(draw2dprogram_->sampler0, 0);
}
std::vector<GLRShader *> shaders;
shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vs_code, "draw2d"));
shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fs_code, "draw2d"));
std::vector<GLRProgram::UniformLocQuery> queries;
queries.push_back({ &u_draw2d_tex, "u_tex" });
std::vector<GLRProgram::Initializer> initializers;
initializers.push_back({ &u_draw2d_tex, 0 });
std::vector<GLRProgram::Semantic> semantics;
semantics.push_back({ 0, "a_position" });
semantics.push_back({ 1, "a_texcoord0" });
draw2dprogram_ = render_->CreateProgram(shaders, semantics, queries, initializers, false);
for (auto shader : shaders)
render_->DeleteShader(shader);
CompilePostShader();
}
}
@@ -155,7 +150,26 @@ void FramebufferManagerGLES::CompilePostShader() {
}
if (!translationFailed) {
postShaderProgram_ = glsl_create_source(vshader.c_str(), fshader.c_str(), &errorString);
SetNumExtraFBOs(1);
std::vector<GLRShader *> shaders;
shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vshader, "postshader"));
shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, fshader, "postshader"));
std::vector<GLRProgram::UniformLocQuery> queries;
queries.push_back({ &u_postShaderTex, "tex" });
queries.push_back({ &deltaLoc_, "u_texelDelta" });
queries.push_back({ &pixelDeltaLoc_, "u_pixelDelta" });
queries.push_back({ &timeLoc_, "u_time" });
queries.push_back({ &videoLoc_, "u_video" });
std::vector<GLRProgram::Initializer> inits;
inits.push_back({ &u_postShaderTex, 0, 0 });
postShaderProgram_ = render_->CreateProgram(shaders, {}, queries, inits, false);
render_->SetUniformI1(&u_postShaderTex, 0);
for (auto iter : shaders) {
render_->DeleteShader(iter);
}
} else {
ERROR_LOG(FRAMEBUF, "Failed to translate post shader!");
}
@@ -193,24 +207,16 @@ void FramebufferManagerGLES::CompilePostShader() {
}
usePostShader_ = false;
} else {
glsl_bind(postShaderProgram_);
glUniform1i(postShaderProgram_->sampler0, 0);
SetNumExtraFBOs(1);
deltaLoc_ = glsl_uniform_loc(postShaderProgram_, "u_texelDelta");
pixelDeltaLoc_ = glsl_uniform_loc(postShaderProgram_, "u_pixelDelta");
timeLoc_ = glsl_uniform_loc(postShaderProgram_, "u_time");
videoLoc_ = glsl_uniform_loc(postShaderProgram_, "u_video");
usePostShader_ = true;
}
} else {
postShaderProgram_ = nullptr;
usePostShader_ = false;
}
glsl_unbind();
}
void FramebufferManagerGLES::Bind2DShader() {
glsl_bind(draw2dprogram_);
render_->BindProgram(draw2dprogram_);
}
void FramebufferManagerGLES::BindPostShader(const PostShaderUniforms &uniforms) {
@@ -218,35 +224,25 @@ void FramebufferManagerGLES::BindPostShader(const PostShaderUniforms &uniforms)
if (!postShaderProgram_) {
CompileDraw2DProgram();
}
glsl_bind(postShaderProgram_);
render_->BindProgram(postShaderProgram_);
if (deltaLoc_ != -1)
glUniform2f(deltaLoc_, uniforms.texelDelta[0], uniforms.texelDelta[1]);
render_->SetUniformF(&deltaLoc_, 2, uniforms.texelDelta);
if (pixelDeltaLoc_ != -1)
glUniform2f(pixelDeltaLoc_, uniforms.pixelDelta[0], uniforms.pixelDelta[1]);
render_->SetUniformF(&pixelDeltaLoc_, 2, uniforms.pixelDelta);
if (timeLoc_ != -1)
glUniform4fv(timeLoc_, 1, uniforms.time);
render_->SetUniformF(&timeLoc_, 4, uniforms.time);
if (videoLoc_ != -1)
glUniform1f(videoLoc_, uniforms.video);
render_->SetUniformF(&videoLoc_, 1, &uniforms.video);
}
FramebufferManagerGLES::FramebufferManagerGLES(Draw::DrawContext *draw) :
FramebufferManagerGLES::FramebufferManagerGLES(Draw::DrawContext *draw, GLRenderManager *render) :
FramebufferManagerCommon(draw),
drawPixelsTex_(0),
drawPixelsTexFormat_(GE_FORMAT_INVALID),
convBuf_(nullptr),
videoLoc_(-1),
timeLoc_(-1),
pixelDeltaLoc_(-1),
deltaLoc_(-1),
textureCacheGL_(nullptr),
shaderManagerGL_(nullptr),
pixelBufObj_(nullptr),
currentPBO_(0)
render_(render)
{
needBackBufferYSwap_ = true;
needGLESRebinds_ = true;
CreateDeviceObjects();
render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
}
void FramebufferManagerGLES::Init() {
@@ -273,23 +269,33 @@ void FramebufferManagerGLES::SetDrawEngine(DrawEngineGLES *td) {
void FramebufferManagerGLES::CreateDeviceObjects() {
CompileDraw2DProgram();
std::vector<GLRInputLayout::Entry> entries;
entries.push_back({ 0, 3, GL_FLOAT, GL_FALSE, sizeof(Simple2DVertex), offsetof(Simple2DVertex, pos) });
entries.push_back({ 1, 2, GL_FLOAT, GL_FALSE, sizeof(Simple2DVertex), offsetof(Simple2DVertex, uv) });
simple2DInputLayout_ = render_->CreateInputLayout(entries);
}
void FramebufferManagerGLES::DestroyDeviceObjects() {
if (simple2DInputLayout_) {
render_->DeleteInputLayout(simple2DInputLayout_);
simple2DInputLayout_ = nullptr;
}
if (draw2dprogram_) {
glsl_destroy(draw2dprogram_);
render_->DeleteProgram(draw2dprogram_);
draw2dprogram_ = nullptr;
}
if (postShaderProgram_) {
glsl_destroy(postShaderProgram_);
render_->DeleteProgram(postShaderProgram_);
postShaderProgram_ = nullptr;
}
if (drawPixelsTex_) {
glDeleteTextures(1, &drawPixelsTex_);
render_->DeleteTexture(drawPixelsTex_);
drawPixelsTex_ = 0;
}
if (stencilUploadProgram_) {
glsl_destroy(stencilUploadProgram_);
render_->DeleteProgram(stencilUploadProgram_);
stencilUploadProgram_ = nullptr;
}
}
@@ -297,12 +303,6 @@ void FramebufferManagerGLES::DestroyDeviceObjects() {
FramebufferManagerGLES::~FramebufferManagerGLES() {
DestroyDeviceObjects();
if (pixelBufObj_) {
for (int i = 0; i < MAX_PBO; i++) {
glDeleteBuffers(1, &pixelBufObj_[i].handle);
}
delete[] pixelBufObj_;
}
delete [] convBuf_;
}
@@ -317,95 +317,68 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma
}
}
if (drawPixelsTex_ && (drawPixelsTexFormat_ != srcPixelFormat || drawPixelsTexW_ != texWidth || drawPixelsTexH_ != height)) {
glDeleteTextures(1, &drawPixelsTex_);
drawPixelsTex_ = 0;
if (drawPixelsTex_) {
render_->DeleteTexture(drawPixelsTex_);
}
if (!drawPixelsTex_) {
drawPixelsTex_ = textureCacheGL_->AllocTextureName();
drawPixelsTexW_ = texWidth;
drawPixelsTexH_ = height;
drawPixelsTex_ = render_->CreateTexture(GL_TEXTURE_2D);
drawPixelsTexW_ = texWidth;
drawPixelsTexH_ = height;
// Initialize backbuffer texture for DrawPixels
glBindTexture(GL_TEXTURE_2D, drawPixelsTex_);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
drawPixelsTexFormat_ = srcPixelFormat;
} else {
glBindTexture(GL_TEXTURE_2D, drawPixelsTex_);
}
drawPixelsTexFormat_ = srcPixelFormat;
// TODO: We can just change the texture format and flip some bits around instead of this.
// Could share code with the texture cache perhaps.
bool useConvBuf = false;
if (srcPixelFormat != GE_FORMAT_8888 || srcStride != texWidth) {
useConvBuf = true;
u32 neededSize = texWidth * height * 4;
if (!convBuf_ || convBufSize_ < neededSize) {
delete [] convBuf_;
convBuf_ = new u8[neededSize];
convBufSize_ = neededSize;
}
for (int y = 0; y < height; y++) {
switch (srcPixelFormat) {
case GE_FORMAT_565:
{
const u16 *src = (const u16 *)srcPixels + srcStride * y;
u8 *dst = convBuf_ + 4 * texWidth * y;
ConvertRGBA565ToRGBA8888((u32 *)dst, src, width);
}
break;
case GE_FORMAT_5551:
{
const u16 *src = (const u16 *)srcPixels + srcStride * y;
u8 *dst = convBuf_ + 4 * texWidth * y;
ConvertRGBA5551ToRGBA8888((u32 *)dst, src, width);
}
break;
case GE_FORMAT_4444:
{
const u16 *src = (const u16 *)srcPixels + srcStride * y;
u8 *dst = convBuf_ + 4 * texWidth * y;
ConvertRGBA4444ToRGBA8888((u32 *)dst, src, width);
}
break;
case GE_FORMAT_8888:
{
const u8 *src = srcPixels + srcStride * 4 * y;
u8 *dst = convBuf_ + 4 * texWidth * y;
memcpy(dst, src, 4 * width);
}
break;
case GE_FORMAT_INVALID:
_dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels().");
break;
u32 neededSize = texWidth * height * 4;
u8 *convBuf = new u8[neededSize];
for (int y = 0; y < height; y++) {
switch (srcPixelFormat) {
case GE_FORMAT_565:
{
const u16 *src = (const u16 *)srcPixels + srcStride * y;
u8 *dst = convBuf + 4 * texWidth * y;
ConvertRGBA565ToRGBA8888((u32 *)dst, src, width);
}
break;
case GE_FORMAT_5551:
{
const u16 *src = (const u16 *)srcPixels + srcStride * y;
u8 *dst = convBuf + 4 * texWidth * y;
ConvertRGBA5551ToRGBA8888((u32 *)dst, src, width);
}
break;
case GE_FORMAT_4444:
{
const u16 *src = (const u16 *)srcPixels + srcStride * y;
u8 *dst = convBuf + 4 * texWidth * y;
ConvertRGBA4444ToRGBA8888((u32 *)dst, src, width);
}
break;
case GE_FORMAT_8888:
{
const u8 *src = srcPixels + srcStride * 4 * y;
u8 *dst = convBuf + 4 * texWidth * y;
memcpy(dst, src, 4 * width);
}
break;
case GE_FORMAT_INVALID:
_dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels().");
break;
}
}
render_->TextureImage(drawPixelsTex_, 0, texWidth, height, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, convBuf, false);
render_->FinalizeTexture(drawPixelsTex_, 0, false);
// Try to skip uploading the unnecessary parts.
if (gstate_c.Supports(GPU_SUPPORTS_UNPACK_SUBIMAGE) && width != texWidth) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, texWidth);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, useConvBuf ? convBuf_ : srcPixels);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
} else {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texWidth, height, GL_RGBA, GL_UNSIGNED_BYTE, useConvBuf ? convBuf_ : srcPixels);
}
CHECK_GL_ERROR_IF_DEBUG();
// TODO: Return instead?
render_->BindTexture(0, drawPixelsTex_);
}
void FramebufferManagerGLES::SetViewport2D(int x, int y, int w, int h) {
glstate.viewport.set(x, y, w, h);
render_->SetViewport({ (float)x, (float)y, (float)w, (float)h, 0.0f, 1.0f });
}
// x, y, w, h are relative coordinates against destW/destH, which is not very intuitive.
@@ -418,7 +391,7 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float
u0,v1,
};
static const GLushort indices[4] = {0,1,3,2};
static const GLushort indices[4] = { 0,1,3,2 };
if (uvRotation != ROTATION_LOCKED_HORIZONTAL) {
float temp[8];
@@ -437,9 +410,9 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float
float pos[12] = {
x,y,0,
x+w,y,0,
x+w,y+h,0,
x,y+h,0
x + w,y,0,
x + w,y + h,0,
x,y + h,0
};
float invDestW = 1.0f / (destW * 0.5f);
@@ -451,42 +424,27 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float
// Upscaling postshaders doesn't look well with linear
if (flags & DRAWTEX_LINEAR) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
render_->SetTextureSampler(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR, 0.0f);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
render_->SetTextureSampler(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f);
}
const GLSLProgram *program = glsl_get_program();
if (!program) {
ERROR_LOG(FRAMEBUF, "Trying to DrawActiveTexture() without a program");
return;
}
Simple2DVertex verts[4];
memcpy(verts[0].pos, &pos[0], 12);
memcpy(verts[1].pos, &pos[3], 12);
memcpy(verts[3].pos, &pos[6], 12);
memcpy(verts[2].pos, &pos[9], 12);
memcpy(verts[0].uv, &texCoords[0], 8);
memcpy(verts[1].uv, &texCoords[2], 8);
memcpy(verts[3].uv, &texCoords[4], 8);
memcpy(verts[2].uv, &texCoords[6], 8);
glEnableVertexAttribArray(program->a_position);
glEnableVertexAttribArray(program->a_texcoord0);
if (gstate_c.Supports(GPU_SUPPORTS_VAO)) {
drawEngineGL_->BindBuffer(pos, sizeof(pos), texCoords, sizeof(texCoords));
drawEngineGL_->BindElementBuffer(indices, sizeof(indices));
glVertexAttribPointer(program->a_position, 3, GL_FLOAT, GL_FALSE, 12, 0);
glVertexAttribPointer(program->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, (void *)sizeof(pos));
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);
} else {
glstate.arrayBuffer.unbind();
glstate.elementArrayBuffer.unbind();
glVertexAttribPointer(program->a_position, 3, GL_FLOAT, GL_FALSE, 12, pos);
glVertexAttribPointer(program->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, texCoords);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices);
}
glDisableVertexAttribArray(program->a_position);
glDisableVertexAttribArray(program->a_texcoord0);
}
void FramebufferManagerGLES::RebindFramebuffer() {
FramebufferManagerCommon::RebindFramebuffer();
if (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE)
glstate.viewport.restore();
uint32_t bindOffset;
GLRBuffer *buffer;
void *dest = drawEngineGL_->GetPushVertexBuffer()->Push(sizeof(verts), &bindOffset, &buffer);
memcpy(dest, verts, sizeof(verts));
render_->BindVertexBuffer(simple2DInputLayout_, buffer, bindOffset);
render_->Draw(GL_TRIANGLE_STRIP, 0, 4);
}
void FramebufferManagerGLES::ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old) {
@@ -505,6 +463,7 @@ void FramebufferManagerGLES::ReformatFramebufferFrom(VirtualFramebuffer *vfb, GE
if (old == GE_FORMAT_565) {
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
gstate_c.Dirty(DIRTY_BLEND_STATE);
} else {
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
}
@@ -527,17 +486,15 @@ void FramebufferManagerGLES::BlitFramebufferDepth(VirtualFramebuffer *src, Virtu
if (gstate_c.Supports(GPU_SUPPORTS_ARB_FRAMEBUFFER_BLIT | GPU_SUPPORTS_NV_FRAMEBUFFER_BLIT)) {
// Let's only do this if not clearing depth.
glstate.scissorTest.force(false);
draw_->BlitFramebuffer(src->fbo, 0, 0, w, h, dst->fbo, 0, 0, w, h, Draw::FB_DEPTH_BIT, Draw::FB_BLIT_NEAREST);
dst->last_frame_depth_updated = gpuStats.numFlips;
glstate.scissorTest.restore();
}
}
}
void FramebufferManagerGLES::BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags) {
if (!framebuffer->fbo || !useBufferedRendering_) {
glBindTexture(GL_TEXTURE_2D, 0);
render_->BindTexture(stage, nullptr);
gstate_c.skipDrawReason |= SKIPDRAW_BAD_FB_TEXTURE;
return;
}
@@ -565,48 +522,6 @@ void FramebufferManagerGLES::BindFramebufferAsColorTexture(int stage, VirtualFra
}
}
void FramebufferManagerGLES::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) {
PROFILE_THIS_SCOPE("gpu-readback");
if (sync) {
// flush async just in case when we go for synchronous update
// Doesn't actually pack when sent a null argument.
PackFramebufferAsync_(nullptr);
}
if (vfb) {
// We'll pseudo-blit framebuffers here to get a resized version of vfb.
VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb);
OptimizeDownloadRange(vfb, x, y, w, h);
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0);
// PackFramebufferSync_() - Synchronous pixel data transfer using glReadPixels
// PackFramebufferAsync_() - Asynchronous pixel data transfer using glReadPixels with PBOs
if (gl_extensions.IsGLES) {
PackFramebufferSync_(nvfb, x, y, w, h);
} else {
// TODO: Can we fall back to sync without these?
if (gl_extensions.ARB_pixel_buffer_object && gstate_c.Supports(GPU_SUPPORTS_OES_TEXTURE_NPOT)) {
if (!sync) {
PackFramebufferAsync_(nvfb);
} else {
PackFramebufferSync_(nvfb, x, y, w, h);
}
}
}
textureCacheGL_->ForgetLastTexture();
RebindFramebuffer();
}
}
void FramebufferManagerGLES::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) {
PROFILE_THIS_SCOPE("gpu-readback");
// Flush async just in case.
PackFramebufferAsync_(nullptr);
FramebufferManagerCommon::DownloadFramebufferForClut(fb_address, loadBytes);
}
bool FramebufferManagerGLES::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) {
// When updating VRAM, it need to be exact format.
if (!gstate_c.Supports(GPU_PREFER_CPU_DOWNLOAD)) {
@@ -641,12 +556,10 @@ void FramebufferManagerGLES::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb)
// Discard the previous contents of this buffer where possible.
if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_STENCIL_ATTACHMENT, GL_DEPTH_ATTACHMENT };
glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, attachments);
} else if (gl_extensions.IsGLES) {
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
gstate_c.Dirty(DIRTY_BLEND_STATE);
}
CHECK_GL_ERROR_IF_DEBUG();
}
void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) {
@@ -699,12 +612,10 @@ void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX,
const bool yOverlap = src == dst && srcY2 > dstY1 && srcY1 < dstY2;
if (sameSize && sameDepth && srcInsideBounds && dstInsideBounds && !(xOverlap && yOverlap)) {
draw_->CopyFramebufferImage(src->fbo, 0, srcX1, srcY1, 0, dst->fbo, 0, dstX1, dstY1, 0, dstX2 - dstX1, dstY2 - dstY1, 1, Draw::FB_COLOR_BIT);
CHECK_GL_ERROR_IF_DEBUG();
return;
}
}
glstate.scissorTest.force(false);
if (useBlit) {
draw_->BlitFramebuffer(src->fbo, srcX1, srcY1, srcX2, srcY2, dst->fbo, dstX1, dstY1, dstX2, dstY2, Draw::FB_COLOR_BIT, Draw::FB_BLIT_NEAREST);
} else {
@@ -714,39 +625,21 @@ void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX,
// Make sure our 2D drawing program is ready. Compiles only if not already compiled.
CompileDraw2DProgram();
glstate.viewport.force(0, 0, dst->renderWidth, dst->renderHeight);
glstate.blend.force(false);
glstate.cullFace.force(false);
glstate.depthTest.force(false);
glstate.stencilTest.force(false);
#if !defined(USING_GLES2)
glstate.colorLogicOp.force(false);
#endif
glstate.colorMask.force(true, true, true, true);
glstate.stencilMask.force(0xFF);
render_->SetViewport({ 0, 0, (float)dst->renderWidth, (float)dst->renderHeight, 0, 1.0f });
render_->SetStencilDisabled();
render_->SetDepth(false, false, GL_ALWAYS);
render_->SetNoBlendAndMask(0xF);
// The first four coordinates are relative to the 6th and 7th arguments of DrawActiveTexture.
// Should maybe revamp that interface.
float srcW = src->bufferWidth;
float srcH = src->bufferHeight;
glsl_bind(draw2dprogram_);
render_->BindProgram(draw2dprogram_);
DrawActiveTexture(dstX1, dstY1, w * dstXFactor, h, dst->bufferWidth, dst->bufferHeight, srcX1 / srcW, srcY1 / srcH, srcX2 / srcW, srcY2 / srcH, ROTATION_LOCKED_HORIZONTAL, DRAWTEX_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
textureCacheGL_->ForgetLastTexture();
glstate.viewport.restore();
glstate.blend.restore();
glstate.cullFace.restore();
glstate.depthTest.restore();
glstate.stencilTest.restore();
#if !defined(USING_GLES2)
glstate.colorLogicOp.restore();
#endif
glstate.colorMask.restore();
glstate.stencilMask.restore();
}
glstate.scissorTest.restore();
CHECK_GL_ERROR_IF_DEBUG();
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE);
}
// TODO: SSE/NEON
@@ -806,191 +699,6 @@ void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u
}
}
void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
CHECK_GL_ERROR_IF_DEBUG();
GLubyte *packed = 0;
bool unbind = false;
const u8 nextPBO = (currentPBO_ + 1) % MAX_PBO;
const bool useCPU = gstate_c.Supports(GPU_PREFER_CPU_DOWNLOAD);
// We'll prepare two PBOs to switch between readying and reading
if (!pixelBufObj_) {
if (!vfb) {
// This call is just to flush the buffers. We don't have any yet,
// so there's nothing to do.
return;
}
GLuint pbos[MAX_PBO];
glGenBuffers(MAX_PBO, pbos);
pixelBufObj_ = new AsyncPBO[MAX_PBO];
for (int i = 0; i < MAX_PBO; i++) {
pixelBufObj_[i].handle = pbos[i];
pixelBufObj_[i].maxSize = 0;
pixelBufObj_[i].reading = false;
}
}
// Receive previously requested data from a PBO
AsyncPBO &pbo = pixelBufObj_[nextPBO];
if (pbo.reading) {
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle);
#ifdef USING_GLES2
// Not on desktop GL 2.x...
packed = (GLubyte *)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, pbo.size, GL_MAP_READ_BIT);
#else
packed = (GLubyte *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
#endif
if (packed) {
DEBUG_LOG(FRAMEBUF, "Reading PBO to memory , bufSize = %u, packed = %p, fb_address = %08x, stride = %u, pbo = %u",
pbo.size, packed, pbo.fb_address, pbo.stride, nextPBO);
if (useCPU) {
u8 *dst = Memory::GetPointer(pbo.fb_address);
ConvertFromRGBA8888(dst, packed, pbo.stride, pbo.stride, pbo.stride, pbo.height, pbo.format);
} else {
// We don't need to convert, GPU already did (or should have)
Memory::MemcpyUnchecked(pbo.fb_address, packed, pbo.size);
}
pbo.reading = false;
}
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
unbind = true;
}
// Order packing/readback of the framebuffer
if (vfb) {
bool reverseOrder = gstate_c.Supports(GPU_PREFER_REVERSE_COLOR_ORDER);
Draw::DataFormat dataFmt = Draw::DataFormat::UNDEFINED;
switch (vfb->format) {
case GE_FORMAT_4444:
dataFmt = (reverseOrder ? Draw::DataFormat::A4R4G4B4_UNORM_PACK16 : Draw::DataFormat::B4G4R4A4_UNORM_PACK16);
break;
case GE_FORMAT_5551:
dataFmt = (reverseOrder ? Draw::DataFormat::A1R5G5B5_UNORM_PACK16 : Draw::DataFormat::B5G5R5A1_UNORM_PACK16);
break;
case GE_FORMAT_565:
dataFmt = (reverseOrder ? Draw::DataFormat::R5G6B5_UNORM_PACK16 : Draw::DataFormat::B5G6R5_UNORM_PACK16);
break;
case GE_FORMAT_8888:
dataFmt = Draw::DataFormat::R8G8B8A8_UNORM;
break;
};
if (useCPU) {
dataFmt = Draw::DataFormat::R8G8B8A8_UNORM;
}
int pixelSize = (int)DataFormatSizeInBytes(dataFmt);
int align = pixelSize;
// If using the CPU, we need 4 bytes per pixel always.
u32 bufSize = vfb->fb_stride * vfb->height * pixelSize;
u32 fb_address = (0x04000000) | vfb->fb_address;
if (!vfb->fbo) {
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferAsync_: vfb->fbo == 0");
return;
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, pixelBufObj_[currentPBO_].handle);
if (pixelBufObj_[currentPBO_].maxSize < bufSize) {
// We reserve a buffer big enough to fit all those pixels
glBufferData(GL_PIXEL_PACK_BUFFER, bufSize, NULL, GL_DYNAMIC_READ);
pixelBufObj_[currentPBO_].maxSize = bufSize;
}
// TODO: This is a hack since PBOs have not been implemented in Thin3D yet (and maybe shouldn't? maybe should do this internally?)
draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, 0, vfb->fb_stride, vfb->height, dataFmt, nullptr, vfb->fb_stride);
unbind = true;
pixelBufObj_[currentPBO_].fb_address = fb_address;
pixelBufObj_[currentPBO_].size = bufSize;
pixelBufObj_[currentPBO_].stride = vfb->fb_stride;
pixelBufObj_[currentPBO_].height = vfb->height;
pixelBufObj_[currentPBO_].format = vfb->format;
pixelBufObj_[currentPBO_].reading = true;
}
currentPBO_ = nextPBO;
if (unbind) {
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
CHECK_GL_ERROR_IF_DEBUG();
}
void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
if (!vfb->fbo) {
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferSync_: vfb->fbo == 0");
return;
}
int possibleH = std::max(vfb->height - y, 0);
if (h > possibleH) {
h = possibleH;
}
bool convert = vfb->format != GE_FORMAT_8888;
const int dstBpp = vfb->format == GE_FORMAT_8888 ? 4 : 2;
const int packWidth = std::min(vfb->fb_stride, std::min(x + w, (int)vfb->width));
// Pixel size always 4 here because we always request RGBA8888
u32 bufSize = packWidth * h * 4;
u32 fb_address = 0x04000000 | vfb->fb_address;
if (gl_extensions.IsGLES && !gl_extensions.GLES3 && packWidth != vfb->fb_stride && h != 1) {
// Need to use a temp buffer, since GLES2 doesn't support GL_PACK_ROW_LENGTH.
convert = true;
}
int dstByteOffset = y * vfb->fb_stride * dstBpp;
u8 *dst = Memory::GetPointer(fb_address + dstByteOffset);
u8 *packed = nullptr;
if (!convert) {
packed = (u8 *)dst;
} else {
// End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address
if (!convBuf_ || convBufSize_ < bufSize) {
delete [] convBuf_;
convBuf_ = new u8[bufSize];
convBufSize_ = bufSize;
}
packed = convBuf_;
}
if (packed) {
DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address);
// Avoid reading the part between width and stride, if possible.
int packStride = convert || h == 1 ? packWidth : vfb->fb_stride;
draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, y, packWidth, h, Draw::DataFormat::R8G8B8A8_UNORM, packed, packStride);
if (convert) {
ConvertFromRGBA8888(dst, packed, vfb->fb_stride, packStride, packWidth, h, vfb->format);
}
}
// TODO: Move this into Thin3d.
if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
#ifdef USING_GLES2
// GLES3 doesn't support using GL_READ_FRAMEBUFFER here.
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
const GLenum target = GL_FRAMEBUFFER;
#else
const GLenum target = GL_READ_FRAMEBUFFER;
#endif
GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT };
glInvalidateFramebuffer(target, 3, attachments);
}
CHECK_GL_ERROR_IF_DEBUG();
}
void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
if (!vfb->fbo) {
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackDepthbuffer: vfb->fbo == 0");
@@ -1031,26 +739,9 @@ void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int
}
}
}
CHECK_GL_ERROR_IF_DEBUG();
}
void FramebufferManagerGLES::EndFrame() {
CHECK_GL_ERROR_IF_DEBUG();
// Let's explicitly invalidate any temp FBOs used during this frame.
if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
for (auto temp : tempFBOs_) {
if (temp.second.last_frame_used < gpuStats.numFlips) {
continue;
}
draw_->BindFramebufferAsRenderTarget(temp.second.fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_STENCIL_ATTACHMENT, GL_DEPTH_ATTACHMENT };
glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, attachments);
}
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP , Draw::RPAction::KEEP, Draw::RPAction::KEEP });
}
CHECK_GL_ERROR_IF_DEBUG();
}
void FramebufferManagerGLES::DeviceLost() {
@@ -1064,7 +755,6 @@ void FramebufferManagerGLES::DeviceRestore(Draw::DrawContext *draw) {
}
void FramebufferManagerGLES::DestroyAllFBOs() {
CHECK_GL_ERROR_IF_DEBUG();
currentRenderVfb_ = 0;
displayFramebuf_ = 0;
prevDisplayFramebuf_ = 0;
@@ -1091,25 +781,17 @@ void FramebufferManagerGLES::DestroyAllFBOs() {
SetNumExtraFBOs(0);
DisableState();
CHECK_GL_ERROR_IF_DEBUG();
}
void FramebufferManagerGLES::Resized() {
FramebufferManagerCommon::Resized();
render_->Resize(PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight);
if (UpdateSize()) {
DestroyAllFBOs();
}
#ifndef USING_GLES2
if (g_Config.iInternalResolution == 0) {
glLineWidth(std::max(1, (int)(renderWidth_ / 480)));
glPointSize(std::max(1.0f, (float)(renderWidth_ / 480.f)));
} else {
glLineWidth(g_Config.iInternalResolution);
glPointSize((float)g_Config.iInternalResolution);
}
#endif
// render_->SetLineWidth(renderWidth_ / 480.0f);
}
bool FramebufferManagerGLES::GetOutputFramebuffer(GPUDebugBuffer &buffer) {
+34 -43
View File
@@ -29,28 +29,16 @@
#include "Core/Config.h"
#include "GPU/GPUCommon.h"
#include "GPU/Common/FramebufferCommon.h"
#include "thin3d/GLRenderManager.h"
struct GLSLProgram;
class TextureCacheGLES;
class DrawEngineGLES;
class ShaderManagerGLES;
// Simple struct for asynchronous PBO readbacks
struct AsyncPBO {
uint32_t handle;
u32 maxSize;
u32 fb_address;
u32 stride;
u32 height;
u32 size;
GEBufferFormat format;
bool reading;
};
class FramebufferManagerGLES : public FramebufferManagerCommon {
public:
FramebufferManagerGLES(Draw::DrawContext *draw);
FramebufferManagerGLES(Draw::DrawContext *draw, GLRenderManager *render);
~FramebufferManagerGLES();
void SetTextureCache(TextureCacheGLES *tc);
@@ -66,7 +54,6 @@ public:
void EndFrame();
void Resized() override;
void DeviceLost();
void SetLineWidth();
void ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old) override;
void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst) override;
@@ -74,14 +61,9 @@ public:
// For use when texturing from a framebuffer. May create a duplicate if target.
void BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags);
// Reads a rectangular subregion of a framebuffer to the right position in its backing memory.
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override;
void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override;
bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override;
bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override;
virtual void RebindFramebuffer() override;
void DeviceRestore(Draw::DrawContext *draw);
@@ -103,35 +85,44 @@ private:
void Bind2DShader() override;
void BindPostShader(const PostShaderUniforms &uniforms) override;
void CompileDraw2DProgram();
void DestroyDraw2DProgram();
void CompilePostShader();
void PackFramebufferAsync_(VirtualFramebuffer *vfb); // Not used under ES currently
void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) override;
void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h);
GLRenderManager *render_;
// Used by DrawPixels
unsigned int drawPixelsTex_;
GEBufferFormat drawPixelsTexFormat_;
int drawPixelsTexW_;
int drawPixelsTexH_;
GLRTexture *drawPixelsTex_ = nullptr;
GEBufferFormat drawPixelsTexFormat_ = GE_FORMAT_INVALID;
int drawPixelsTexW_ = 0;
int drawPixelsTexH_ = 0;
u8 *convBuf_;
u32 convBufSize_;
GLSLProgram *draw2dprogram_ = nullptr;
GLSLProgram *postShaderProgram_ = nullptr;
GLSLProgram *stencilUploadProgram_ = nullptr;
int plainColorLoc_;
int videoLoc_;
int timeLoc_;
int pixelDeltaLoc_;
int deltaLoc_;
u8 *convBuf_ = nullptr;
u32 convBufSize_ = 0;
GLRProgram *draw2dprogram_ = nullptr;
GLRProgram *postShaderProgram_ = nullptr;
TextureCacheGLES *textureCacheGL_;
ShaderManagerGLES *shaderManagerGL_;
DrawEngineGLES *drawEngineGL_;
GLRProgram *stencilUploadProgram_ = nullptr;
int u_stencilUploadTex = -1;
int u_stencilValue = -1;
int u_postShaderTex = -1;
// Cached uniform locs
int u_draw2d_tex = -1;
// Not used under ES currently.
AsyncPBO *pixelBufObj_; //this isn't that large
u8 currentPBO_;
int plainColorLoc_ = -1;
int videoLoc_ = -1;
int timeLoc_ = -1;
int pixelDeltaLoc_ = -1;
int deltaLoc_ = -1;
TextureCacheGLES *textureCacheGL_ = nullptr;
ShaderManagerGLES *shaderManagerGL_ = nullptr;
DrawEngineGLES *drawEngineGL_ = nullptr;
struct Simple2DVertex {
float pos[3];
float uv[2];
};
GLRInputLayout *simple2DInputLayout_ = nullptr;
};
+30 -45
View File
@@ -36,7 +36,6 @@
#include "GPU/GeDisasm.h"
#include "GPU/Common/FramebufferCommon.h"
#include "ext/native/gfx/GLStateCache.h"
#include "GPU/GLES/ShaderManagerGLES.h"
#include "GPU/GLES/GPU_GLES.h"
#include "GPU/GLES/FramebufferManagerGLES.h"
@@ -79,12 +78,14 @@ static const GLESCommandTableEntry commandTable[] = {
GPU_GLES::CommandInfo GPU_GLES::cmdInfo_[256];
GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
: GPUCommon(gfxCtx, draw) {
: GPUCommon(gfxCtx, draw), drawEngine_(draw), fragmentTestCache_(draw), depalShaderCache_(draw) {
UpdateVsyncInterval(true);
CheckGPUFeatures();
shaderManagerGL_ = new ShaderManagerGLES();
framebufferManagerGL_ = new FramebufferManagerGLES(draw);
GLRenderManager *render = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
shaderManagerGL_ = new ShaderManagerGLES(render);
framebufferManagerGL_ = new FramebufferManagerGLES(draw, render);
framebufferManager_ = framebufferManagerGL_;
textureCacheGL_ = new TextureCacheGLES(draw);
textureCache_ = textureCacheGL_;
@@ -158,10 +159,6 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
// Update again after init to be sure of any silly driver problems.
UpdateVsyncInterval(true);
// Some of our defaults are different from hw defaults, let's assert them.
// We restore each frame anyway, but here is convenient for tests.
glstate.Restore();
drawEngine_.RestoreVAO();
textureCacheGL_->NotifyConfigChanged();
// Load shader cache.
@@ -187,6 +184,10 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
}
GPU_GLES::~GPU_GLES() {
GLRenderManager *render = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
render->Wipe();
render->WaitUntilQueueIdle();
framebufferManagerGL_->DestroyAllFBOs();
shaderManagerGL_->ClearCache(true);
depalShaderCache_.Clear();
@@ -298,9 +299,6 @@ void GPU_GLES::CheckGPUFeatures() {
if (gl_extensions.OES_texture_npot)
features |= GPU_SUPPORTS_OES_TEXTURE_NPOT;
if (gl_extensions.EXT_unpack_subimage)
features |= GPU_SUPPORTS_UNPACK_SUBIMAGE;
if (gl_extensions.EXT_blend_minmax)
features |= GPU_SUPPORTS_BLEND_MINMAX;
@@ -322,8 +320,7 @@ void GPU_GLES::CheckGPUFeatures() {
if (instanceRendering)
features |= GPU_SUPPORTS_INSTANCE_RENDERING;
int maxVertexTextureImageUnits;
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextureImageUnits);
int maxVertexTextureImageUnits = gl_extensions.maxVertexTextureUnits;
if (maxVertexTextureImageUnits >= 3) // At least 3 for hardware tessellation
features |= GPU_SUPPORTS_VERTEX_TEXTURE_FETCH;
@@ -368,30 +365,24 @@ bool GPU_GLES::IsReady() {
return shaderManagerGL_->ContinuePrecompile();
}
// Let's avoid passing nulls into snprintf().
static const char *GetGLStringAlways(GLenum name) {
const GLubyte *value = glGetString(name);
if (!value)
return "?";
return (const char *)value;
}
// Needs to be called on GPU thread, not reporting thread.
void GPU_GLES::BuildReportingInfo() {
const char *glVendor = GetGLStringAlways(GL_VENDOR);
const char *glRenderer = GetGLStringAlways(GL_RENDERER);
const char *glVersion = GetGLStringAlways(GL_VERSION);
const char *glSlVersion = GetGLStringAlways(GL_SHADING_LANGUAGE_VERSION);
const char *glExtensions = nullptr;
GLRenderManager *render = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
std::string glVendor = render->GetGLString(GL_VENDOR);
std::string glRenderer = render->GetGLString(GL_RENDERER);
std::string glVersion = render->GetGLString(GL_VERSION);
std::string glSlVersion = render->GetGLString(GL_SHADING_LANGUAGE_VERSION);
std::string glExtensions;
if (gl_extensions.VersionGEThan(3, 0)) {
glExtensions = g_all_gl_extensions.c_str();
glExtensions = g_all_gl_extensions;
} else {
glExtensions = GetGLStringAlways(GL_EXTENSIONS);
glExtensions = render->GetGLString(GL_EXTENSIONS);
}
char temp[16384];
snprintf(temp, sizeof(temp), "%s (%s %s), %s (extensions: %s)", glVersion, glVendor, glRenderer, glSlVersion, glExtensions);
snprintf(temp, sizeof(temp), "%s (%s %s), %s (extensions: %s)", glVersion.c_str(), glVendor.c_str(), glRenderer.c_str(), glSlVersion.c_str(), glExtensions.c_str());
reportingPrimaryInfo_ = glVendor;
reportingFullInfo_ = temp;
@@ -401,6 +392,10 @@ void GPU_GLES::BuildReportingInfo() {
void GPU_GLES::DeviceLost() {
ILOG("GPU_GLES: DeviceLost");
// Simply drop all caches and textures.
// FBOs appear to survive? Or no?
// TransformDraw has registered as a GfxResourceHolder.
drawEngine_.ClearInputLayoutMap();
shaderManagerGL_->ClearCache(false);
textureCacheGL_->Clear(false);
fragmentTestCache_.Clear(false);
@@ -429,14 +424,6 @@ void GPU_GLES::Reinitialize() {
}
void GPU_GLES::InitClear() {
bool useNonBufferedRendering = g_Config.iRenderingMode == FB_NON_BUFFERED_MODE;
if (useNonBufferedRendering) {
glstate.depthWrite.set(GL_TRUE);
glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
glstate.viewport.set(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight);
}
void GPU_GLES::BeginHostFrame() {
@@ -449,6 +436,12 @@ void GPU_GLES::BeginHostFrame() {
shaderManagerGL_->DirtyShader();
textureCacheGL_->NotifyConfigChanged();
}
drawEngine_.BeginFrame();
}
void GPU_GLES::EndHostFrame() {
drawEngine_.EndFrame();
}
inline void GPU_GLES::UpdateVsyncInterval(bool force) {
@@ -489,8 +482,6 @@ void GPU_GLES::UpdateCmdInfo() {
}
void GPU_GLES::ReapplyGfxState() {
drawEngine_.RestoreVAO();
glstate.Restore();
GPUCommon::ReapplyGfxState();
}
@@ -500,7 +491,6 @@ void GPU_GLES::BeginFrame() {
textureCacheGL_->StartFrame();
drawEngine_.DecimateTrackedVertexArrays();
drawEngine_.DecimateBuffers();
depalShaderCache_.Decimate();
fragmentTestCache_.Decimate();
@@ -551,9 +541,6 @@ void GPU_GLES::CopyDisplayToOutput() {
shaderManagerGL_->DirtyLastShader();
glstate.depthWrite.set(GL_TRUE);
glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
framebufferManagerGL_->CopyDisplayToOutput();
framebufferManagerGL_->EndFrame();
@@ -762,8 +749,6 @@ void GPU_GLES::ClearShaderCache() {
void GPU_GLES::CleanupBeforeUI() {
// Clear any enabled vertex arrays.
shaderManagerGL_->DirtyLastShader();
glstate.arrayBuffer.bind(0);
glstate.elementArrayBuffer.bind(0);
}
void GPU_GLES::DoState(PointerWrap &p) {
+1
View File
@@ -80,6 +80,7 @@ public:
std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override;
void BeginHostFrame() override;
void EndHostFrame() override;
protected:
void FastRunLoop(DisplayList &list) override;
+161 -300
View File
@@ -26,6 +26,8 @@
#include "base/logging.h"
#include "base/timeutil.h"
#include "gfx/gl_debug_log.h"
#include "gfx_es2/gpu_features.h"
#include "thin3d/GLRenderManager.h"
#include "i18n/i18n.h"
#include "math/math_util.h"
#include "math/lin/matrix4x4.h"
@@ -38,13 +40,12 @@
#include "GPU/Math3D.h"
#include "GPU/GPUState.h"
#include "GPU/ge_constants.h"
#include "ext/native/gfx/GLStateCache.h"
#include "GPU/GLES/ShaderManagerGLES.h"
#include "GPU/GLES/DrawEngineGLES.h"
#include "FramebufferManagerGLES.h"
Shader::Shader(const ShaderID &id, const char *code, uint32_t glShaderType, bool useHWTransform, uint32_t attrMask, uint64_t uniformMask)
: failed_(false), useHWTransform_(useHWTransform), attrMask_(attrMask), uniformMask_(uniformMask) {
Shader::Shader(GLRenderManager *render, const char *code, const std::string &desc, uint32_t glShaderType, bool useHWTransform, uint32_t attrMask, uint64_t uniformMask)
: render_(render), failed_(false), useHWTransform_(useHWTransform), attrMask_(attrMask), uniformMask_(uniformMask) {
PROFILE_THIS_SCOPE("shadercomp");
isFragment_ = glShaderType == GL_FRAGMENT_SHADER;
source_ = code;
@@ -55,244 +56,153 @@ Shader::Shader(const ShaderID &id, const char *code, uint32_t glShaderType, bool
printf("%s\n", code);
#endif
#endif
shader = glCreateShader(glShaderType);
glShaderSource(shader, 1, &code, 0);
glCompileShader(shader);
GLint success = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
#define MAX_INFO_LOG_SIZE 2048
GLchar infoLog[MAX_INFO_LOG_SIZE];
GLsizei len;
glGetShaderInfoLog(shader, MAX_INFO_LOG_SIZE, &len, infoLog);
infoLog[len] = '\0';
#ifdef __ANDROID__
ELOG("Error in shader compilation! %s\n", infoLog);
ELOG("Shader source:\n%s\n", (const char *)code);
#endif
std::string desc = GetShaderString(SHADER_STRING_SHORT_DESC, id);
ERROR_LOG(G3D, "Error in shader compilation for: %s", desc.c_str());
ERROR_LOG(G3D, "Info log: %s", infoLog);
ERROR_LOG(G3D, "Shader source:\n%s\n", (const char *)code);
Reporting::ReportMessage("Error in shader compilation: info: %s\n%s\n%s", infoLog, desc.c_str(), (const char *)code);
#ifdef SHADERLOG
OutputDebugStringUTF8(infoLog);
#endif
failed_ = true;
shader = 0;
} else {
DEBUG_LOG(G3D, "Compiled shader:\n%s\n", (const char *)code);
}
CHECK_GL_ERROR_IF_DEBUG();
shader = render->CreateShader(glShaderType, source_, desc);
}
Shader::~Shader() {
if (shader)
glDeleteShader(shader);
render_->DeleteShader(shader);
}
LinkedShader::LinkedShader(VShaderID VSID, Shader *vs, FShaderID FSID, Shader *fs, bool useHWTransform, bool preloading)
: useHWTransform_(useHWTransform) {
LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, FShaderID FSID, Shader *fs, bool useHWTransform, bool preloading)
: render_(render), useHWTransform_(useHWTransform) {
PROFILE_THIS_SCOPE("shaderlink");
program = glCreateProgram();
vs_ = vs;
glAttachShader(program, vs->shader);
glAttachShader(program, fs->shader);
// Bind attribute locations to fixed locations so that they're
// the same in all shaders. We use this later to minimize the calls to
// glEnableVertexAttribArray and glDisableVertexAttribArray.
glBindAttribLocation(program, ATTR_POSITION, "position");
glBindAttribLocation(program, ATTR_TEXCOORD, "texcoord");
glBindAttribLocation(program, ATTR_NORMAL, "normal");
glBindAttribLocation(program, ATTR_W1, "w1");
glBindAttribLocation(program, ATTR_W2, "w2");
glBindAttribLocation(program, ATTR_COLOR0, "color0");
glBindAttribLocation(program, ATTR_COLOR1, "color1");
std::vector<GLRShader *> shaders;
shaders.push_back(vs->shader);
shaders.push_back(fs->shader);
#if !defined(USING_GLES2)
if (gstate_c.Supports(GPU_SUPPORTS_DUALSOURCE_BLEND)) {
// Dual source alpha
glBindFragDataLocationIndexed(program, 0, 0, "fragColor0");
glBindFragDataLocationIndexed(program, 0, 1, "fragColor1");
} else if (gl_extensions.VersionGEThan(3, 3, 0)) {
glBindFragDataLocation(program, 0, "fragColor0");
}
#elif !defined(IOS)
if (gl_extensions.GLES3) {
if (gstate_c.Supports(GPU_SUPPORTS_DUALSOURCE_BLEND)) {
glBindFragDataLocationIndexedEXT(program, 0, 0, "fragColor0");
glBindFragDataLocationIndexedEXT(program, 0, 1, "fragColor1");
}
}
#endif
glLinkProgram(program);
std::vector<GLRProgram::Semantic> semantics;
semantics.push_back({ ATTR_POSITION, "position" });
semantics.push_back({ ATTR_TEXCOORD, "texcoord" });
semantics.push_back({ ATTR_NORMAL, "normal" });
semantics.push_back({ ATTR_W1, "w1" });
semantics.push_back({ ATTR_W2, "w2" });
semantics.push_back({ ATTR_COLOR0, "color0" });
semantics.push_back({ ATTR_COLOR1, "color1" });
GLint linkStatus = GL_FALSE;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus != GL_TRUE) {
GLint bufLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
if (bufLength) {
char* buf = new char[bufLength];
glGetProgramInfoLog(program, bufLength, NULL, buf);
#ifdef __ANDROID__
ELOG("Could not link program:\n %s", buf);
#endif
ERROR_LOG(G3D, "Could not link program:\n %s", buf);
std::vector<GLRProgram::UniformLocQuery> queries;
queries.push_back({ &u_tex, "tex" });
queries.push_back({ &u_proj, "u_proj" });
queries.push_back({ &u_proj_through, "u_proj_through" });
std::string vs_desc = vs->GetShaderString(SHADER_STRING_SHORT_DESC, VSID);
std::string fs_desc = fs->GetShaderString(SHADER_STRING_SHORT_DESC, FSID);
std::string vs_source = vs->GetShaderString(SHADER_STRING_SOURCE_CODE, VSID);
std::string fs_source = fs->GetShaderString(SHADER_STRING_SOURCE_CODE, FSID);
queries.push_back({ &u_proj, "u_proj" });
queries.push_back({ &u_proj_through, "u_proj_through" });
queries.push_back({ &u_texenv, "u_texenv" });
queries.push_back({ &u_fogcolor, "u_fogcolor" });
queries.push_back({ &u_fogcoef, "u_fogcoef" });
queries.push_back({ &u_alphacolorref, "u_alphacolorref" });
queries.push_back({ &u_alphacolormask, "u_alphacolormask" });
queries.push_back({ &u_stencilReplaceValue, "u_stencilReplaceValue" });
queries.push_back({ &u_testtex, "testtex" });
ERROR_LOG(G3D, "VS desc:\n%s", vs_desc.c_str());
ERROR_LOG(G3D, "FS desc:\n%s", fs_desc.c_str());
ERROR_LOG(G3D, "VS:\n%s\n", vs_source.c_str());
ERROR_LOG(G3D, "FS:\n%s\n", fs_source.c_str());
if (preloading) {
Reporting::ReportMessage("Error in shader program link during preload: info: %s\nfs: %s\n%s\nvs: %s\n%s", buf, fs_desc.c_str(), fs_source.c_str(), vs_desc.c_str(), vs_source.c_str());
} else {
Reporting::ReportMessage("Error in shader program link: info: %s\nfs: %s\n%s\nvs: %s\n%s", buf, fs_desc.c_str(), fs_source.c_str(), vs_desc.c_str(), vs_source.c_str());
}
#ifdef SHADERLOG
OutputDebugStringUTF8(buf);
OutputDebugStringUTF8(vs_source.c_str());
OutputDebugStringUTF8(fs_source.c_str());
#endif
delete [] buf; // we're dead!
}
// Prevent a buffer overflow.
numBones = 0;
// Avoid weird attribute enables.
attrMask = 0;
availableUniforms = 0;
return;
}
INFO_LOG(G3D, "Linked shader: vs %i fs %i", (int)vs->shader, (int)fs->shader);
u_tex = glGetUniformLocation(program, "tex");
u_proj = glGetUniformLocation(program, "u_proj");
u_proj_through = glGetUniformLocation(program, "u_proj_through");
u_texenv = glGetUniformLocation(program, "u_texenv");
u_fogcolor = glGetUniformLocation(program, "u_fogcolor");
u_fogcoef = glGetUniformLocation(program, "u_fogcoef");
u_alphacolorref = glGetUniformLocation(program, "u_alphacolorref");
u_alphacolormask = glGetUniformLocation(program, "u_alphacolormask");
u_stencilReplaceValue = glGetUniformLocation(program, "u_stencilReplaceValue");
u_testtex = glGetUniformLocation(program, "testtex");
u_fbotex = glGetUniformLocation(program, "fbotex");
u_blendFixA = glGetUniformLocation(program, "u_blendFixA");
u_blendFixB = glGetUniformLocation(program, "u_blendFixB");
u_fbotexSize = glGetUniformLocation(program, "u_fbotexSize");
queries.push_back({ &u_fbotex, "fbotex" });
queries.push_back({ &u_blendFixA, "u_blendFixA" });
queries.push_back({ &u_blendFixB, "u_blendFixB" });
queries.push_back({ &u_fbotexSize, "u_fbotexSize" });
// Transform
u_view = glGetUniformLocation(program, "u_view");
u_world = glGetUniformLocation(program, "u_world");
u_texmtx = glGetUniformLocation(program, "u_texmtx");
queries.push_back({ &u_view, "u_view" });
queries.push_back({ &u_world, "u_world" });
queries.push_back({ &u_texmtx, "u_texmtx" });
if (VSID.Bit(VS_BIT_ENABLE_BONES))
numBones = TranslateNumBones(VSID.Bits(VS_BIT_BONES, 3) + 1);
else
numBones = 0;
u_depthRange = glGetUniformLocation(program, "u_depthRange");
queries.push_back({ &u_depthRange, "u_depthRange" });
#ifdef USE_BONE_ARRAY
u_bone = glGetUniformLocation(program, "u_bone");
queries.push_back({ &u_bone, "u_bone" });
#else
static const char * const boneNames[8] = { "u_bone0", "u_bone1", "u_bone2", "u_bone3", "u_bone4", "u_bone5", "u_bone6", "u_bone7", };
for (int i = 0; i < 8; i++) {
char name[10];
sprintf(name, "u_bone%i", i);
u_bone[i] = glGetUniformLocation(program, name);
queries.push_back({ &u_bone[i], boneNames[i] });
}
#endif
// Lighting, texturing
u_ambient = glGetUniformLocation(program, "u_ambient");
u_matambientalpha = glGetUniformLocation(program, "u_matambientalpha");
u_matdiffuse = glGetUniformLocation(program, "u_matdiffuse");
u_matspecular = glGetUniformLocation(program, "u_matspecular");
u_matemissive = glGetUniformLocation(program, "u_matemissive");
u_uvscaleoffset = glGetUniformLocation(program, "u_uvscaleoffset");
u_texclamp = glGetUniformLocation(program, "u_texclamp");
u_texclampoff = glGetUniformLocation(program, "u_texclampoff");
queries.push_back({ &u_ambient, "u_ambient" });
queries.push_back({ &u_matambientalpha, "u_matambientalpha" });
queries.push_back({ &u_matdiffuse, "u_matdiffuse" });
queries.push_back({ &u_matspecular, "u_matspecular" });
queries.push_back({ &u_matemissive, "u_matemissive" });
queries.push_back({ &u_uvscaleoffset, "u_uvscaleoffset" });
queries.push_back({ &u_texclamp, "u_texclamp" });
queries.push_back({ &u_texclampoff, "u_texclampoff" });
for (int i = 0; i < 4; i++) {
char temp[64];
sprintf(temp, "u_lightpos%i", i);
u_lightpos[i] = glGetUniformLocation(program, temp);
sprintf(temp, "u_lightdir%i", i);
u_lightdir[i] = glGetUniformLocation(program, temp);
sprintf(temp, "u_lightatt%i", i);
u_lightatt[i] = glGetUniformLocation(program, temp);
sprintf(temp, "u_lightangle%i", i);
u_lightangle[i] = glGetUniformLocation(program, temp);
sprintf(temp, "u_lightspotCoef%i", i);
u_lightspotCoef[i] = glGetUniformLocation(program, temp);
sprintf(temp, "u_lightambient%i", i);
u_lightambient[i] = glGetUniformLocation(program, temp);
sprintf(temp, "u_lightdiffuse%i", i);
u_lightdiffuse[i] = glGetUniformLocation(program, temp);
sprintf(temp, "u_lightspecular%i", i);
u_lightspecular[i] = glGetUniformLocation(program, temp);
static const char * const lightPosNames[4] = { "u_lightpos0", "u_lightpos1", "u_lightpos2", "u_lightpos3", };
queries.push_back({ &u_lightpos[i], lightPosNames[i] });
static const char * const lightdir_names[4] = { "u_lightdir0", "u_lightdir1", "u_lightdir2", "u_lightdir3", };
queries.push_back({ &u_lightdir[i], lightdir_names[i] });
static const char * const lightatt_names[4] = { "u_lightatt0", "u_lightatt1", "u_lightatt2", "u_lightatt3", };
queries.push_back({ &u_lightatt[i], lightatt_names[i] });
static const char * const lightangle_names[4] = { "u_lightangle0", "u_lightangle1", "u_lightangle2", "u_lightangle3", };
queries.push_back({ &u_lightangle[i], lightangle_names[i] });
static const char * const lightspotCoef_names[4] = { "u_lightspotCoef0", "u_lightspotCoef1", "u_lightspotCoef2", "u_lightspotCoef3", };
queries.push_back({ &u_lightspotCoef[i], lightspotCoef_names[i] });
static const char * const lightambient_names[4] = { "u_lightambient0", "u_lightambient1", "u_lightambient2", "u_lightambient3", };
queries.push_back({ &u_lightambient[i], lightambient_names[i] });
static const char * const lightdiffuse_names[4] = { "u_lightdiffuse0", "u_lightdiffuse1", "u_lightdiffuse2", "u_lightdiffuse3", };
queries.push_back({ &u_lightdiffuse[i], lightdiffuse_names[i] });
static const char * const lightspecular_names[4] = { "u_lightspecular0", "u_lightspecular1", "u_lightspecular2", "u_lightspecular3", };
queries.push_back({ &u_lightspecular[i], lightspecular_names[i] });
}
// We need to fetch these unconditionally, gstate_c.spline or bezier will not be set if we
// create this shader at load time from the shader cache.
u_tess_pos_tex = glGetUniformLocation(program, "u_tess_pos_tex");
u_tess_tex_tex = glGetUniformLocation(program, "u_tess_tex_tex");
u_tess_col_tex = glGetUniformLocation(program, "u_tess_col_tex");
u_spline_count_u = glGetUniformLocation(program, "u_spline_count_u");
u_spline_count_v = glGetUniformLocation(program, "u_spline_count_v");
u_spline_type_u = glGetUniformLocation(program, "u_spline_type_u");
u_spline_type_v = glGetUniformLocation(program, "u_spline_type_v");
queries.push_back({ &u_tess_pos_tex, "u_tess_pos_tex" });
queries.push_back({ &u_tess_tex_tex, "u_tess_tex_tex" });
queries.push_back({ &u_tess_col_tex, "u_tess_col_tex" });
queries.push_back({ &u_spline_count_u, "u_spline_count_u" });
queries.push_back({ &u_spline_count_v, "u_spline_count_v" });
queries.push_back({ &u_spline_type_u, "u_spline_type_u" });
queries.push_back({ &u_spline_type_v, "u_spline_type_v" });
attrMask = vs->GetAttrMask();
availableUniforms = vs->GetUniformMask() | fs->GetUniformMask();
glUseProgram(program);
std::vector<GLRProgram::Initializer> initialize;
initialize.push_back({ &u_tex, 0, 0 });
initialize.push_back({ &u_fbotex, 0, 1 });
initialize.push_back({ &u_testtex, 0, 2 });
initialize.push_back({ &u_tess_pos_tex, 0, 4 }); // Texture unit 4
initialize.push_back({ &u_tess_tex_tex, 0, 5 }); // Texture unit 5
initialize.push_back({ &u_tess_col_tex, 0, 6 }); // Texture unit 6
// Default uniform values
glUniform1i(u_tex, 0);
glUniform1i(u_fbotex, 1);
glUniform1i(u_testtex, 2);
if (u_tess_pos_tex != -1)
glUniform1i(u_tess_pos_tex, 4); // Texture unit 4
if (u_tess_tex_tex != -1)
glUniform1i(u_tess_tex_tex, 5); // Texture unit 5
if (u_tess_col_tex != -1)
glUniform1i(u_tess_col_tex, 6); // Texture unit 6
program = render->CreateProgram(shaders, semantics, queries, initialize, gstate_c.featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND);
// The rest, use the "dirty" mechanism.
dirtyUniforms = DIRTY_ALL_UNIFORMS;
CHECK_GL_ERROR_IF_DEBUG();
}
LinkedShader::~LinkedShader() {
// Shaders are automatically detached by glDeleteProgram.
glDeleteProgram(program);
render_->DeleteProgram(program);
}
// Utility
static void SetColorUniform3(int uniform, u32 color) {
float f[4];
Uint8x4ToFloat4(f, color);
glUniform3fv(uniform, 1, f);
static inline void SetFloatUniform(GLRenderManager *render, GLint *uniform, float value) {
render->SetUniformF(uniform, 1, &value);
}
static void SetColorUniform3Alpha(int uniform, u32 color, u8 alpha) {
static inline void SetColorUniform3(GLRenderManager *render, GLint *uniform, u32 color) {
float f[4];
Uint8x4ToFloat4(f, color);
render->SetUniformF(uniform, 3, f);
}
static void SetColorUniform3Alpha(GLRenderManager *render, GLint *uniform, u32 color, u8 alpha) {
float f[4];
Uint8x3ToFloat4_AlphaUint8(f, color, alpha);
glUniform4fv(uniform, 1, f);
render->SetUniformF(uniform, 4, f);
}
// This passes colors unscaled (e.g. 0 - 255 not 0 - 1.)
static void SetColorUniform3Alpha255(int uniform, u32 color, u8 alpha) {
static void SetColorUniform3Alpha255(GLRenderManager *render, GLint *uniform, u32 color, u8 alpha) {
if (gl_extensions.gpuVendor == GPU_VENDOR_IMGTEC) {
const float col[4] = {
(float)((color & 0xFF) >> 0) * (1.0f / 255.0f),
@@ -300,7 +210,7 @@ static void SetColorUniform3Alpha255(int uniform, u32 color, u8 alpha) {
(float)((color & 0xFF0000) >> 16) * (1.0f / 255.0f),
(float)alpha * (1.0f / 255.0f)
};
glUniform4fv(uniform, 1, col);
render->SetUniformF(uniform, 4, col);
} else {
const float col[4] = {
(float)((color & 0xFF) >> 0),
@@ -308,44 +218,44 @@ static void SetColorUniform3Alpha255(int uniform, u32 color, u8 alpha) {
(float)((color & 0xFF0000) >> 16),
(float)alpha
};
glUniform4fv(uniform, 1, col);
render->SetUniformF(uniform, 4, col);
}
}
static void SetColorUniform3iAlpha(int uniform, u32 color, u8 alpha) {
static void SetColorUniform3iAlpha(GLRenderManager *render, GLint *uniform, u32 color, u8 alpha) {
const int col[4] = {
(int)((color & 0xFF) >> 0),
(int)((color & 0xFF00) >> 8),
(int)((color & 0xFF0000) >> 16),
(int)alpha,
};
glUniform4iv(uniform, 1, col);
render->SetUniformI(uniform, 4, col);
}
static void SetColorUniform3ExtraFloat(int uniform, u32 color, float extra) {
static void SetColorUniform3ExtraFloat(GLRenderManager *render, GLint *uniform, u32 color, float extra) {
const float col[4] = {
((color & 0xFF)) / 255.0f,
((color & 0xFF00) >> 8) / 255.0f,
((color & 0xFF0000) >> 16) / 255.0f,
extra
};
glUniform4fv(uniform, 1, col);
render->SetUniformF(uniform, 4, col);
}
static void SetFloat24Uniform3(int uniform, const uint32_t data[3]) {
static void SetFloat24Uniform3(GLRenderManager *render, GLint *uniform, const uint32_t data[3]) {
float f[4];
ExpandFloat24x3ToFloat4(f, data);
glUniform3fv(uniform, 1, f);
render->SetUniformF(uniform, 3, f);
}
static void SetFloatUniform4(int uniform, float data[4]) {
glUniform4fv(uniform, 1, data);
static void SetFloatUniform4(GLRenderManager *render, GLint *uniform, float data[4]) {
render->SetUniformF(uniform, 4, data);
}
static void SetMatrix4x3(int uniform, const float *m4x3) {
static void SetMatrix4x3(GLRenderManager *render, GLint *uniform, const float *m4x3) {
float m4x4[16];
ConvertMatrix4x3To4x4(m4x4, m4x3);
glUniformMatrix4fv(uniform, 1, GL_FALSE, m4x4);
render->SetUniformM4x4(uniform, m4x4);
}
static inline void ScaleProjMatrix(Matrix4x4 &in) {
@@ -359,34 +269,12 @@ static inline void ScaleProjMatrix(Matrix4x4 &in) {
in.translateAndScale(trans, scale);
}
void LinkedShader::use(const VShaderID &VSID, LinkedShader *previous) {
glUseProgram(program);
int enable, disable;
if (previous) {
enable = attrMask & ~previous->attrMask;
disable = (~attrMask) & previous->attrMask;
} else {
enable = attrMask;
disable = ~attrMask;
}
for (int i = 0; i < ATTR_COUNT; i++) {
if (enable & (1 << i))
glEnableVertexAttribArray(i);
else if (disable & (1 << i))
glDisableVertexAttribArray(i);
}
void LinkedShader::use(const ShaderID &VSID) {
render_->BindProgram(program);
// Note that we no longer track attr masks here - we do it for the input layouts instead.
}
void LinkedShader::stop() {
for (int i = 0; i < ATTR_COUNT; i++) {
if (attrMask & (1 << i))
glDisableVertexAttribArray(i);
}
}
void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
CHECK_GL_ERROR_IF_DEBUG();
void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid) {
u64 dirty = dirtyUniforms & availableUniforms;
dirtyUniforms = 0;
if (!dirty)
@@ -448,7 +336,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
ScaleProjMatrix(flippedMatrix);
glUniformMatrix4fv(u_proj, 1, GL_FALSE, flippedMatrix.m);
render_->SetUniformM4x4(&u_proj, flippedMatrix.m);
}
if (dirty & DIRTY_PROJTHROUGHMATRIX)
{
@@ -459,19 +347,19 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
} else {
proj_through.setOrtho(0.0f, gstate_c.curRTWidth, gstate_c.curRTHeight, 0.0f, 0.0f, 1.0f);
}
glUniformMatrix4fv(u_proj_through, 1, GL_FALSE, proj_through.getReadPtr());
render_->SetUniformM4x4(&u_proj_through, proj_through.getReadPtr());
}
if (dirty & DIRTY_TEXENV) {
SetColorUniform3(u_texenv, gstate.texenvcolor);
SetColorUniform3(render_, &u_texenv, gstate.texenvcolor);
}
if (dirty & DIRTY_ALPHACOLORREF) {
SetColorUniform3Alpha255(u_alphacolorref, gstate.getColorTestRef(), gstate.getAlphaTestRef() & gstate.getAlphaTestMask());
SetColorUniform3Alpha255(render_, &u_alphacolorref, gstate.getColorTestRef(), gstate.getAlphaTestRef() & gstate.getAlphaTestMask());
}
if (dirty & DIRTY_ALPHACOLORMASK) {
SetColorUniform3iAlpha(u_alphacolormask, gstate.colortestmask, gstate.getAlphaTestMask());
SetColorUniform3iAlpha(render_, &u_alphacolormask, gstate.colortestmask, gstate.getAlphaTestMask());
}
if (dirty & DIRTY_FOGCOLOR) {
SetColorUniform3(u_fogcolor, gstate.fogcolor);
SetColorUniform3(render_, &u_fogcolor, gstate.fogcolor);
}
if (dirty & DIRTY_FOGCOEF) {
float fogcoef[2] = {
@@ -494,7 +382,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
ERROR_LOG_REPORT_ONCE(fognan, G3D, "Unhandled fog NaN/INF combo: %f %f", fogcoef[0], fogcoef[1]);
}
#endif
glUniform2fv(u_fogcoef, 1, fogcoef);
render_->SetUniformF(&u_fogcoef, 2, fogcoef);
}
if (dirty & DIRTY_UVSCALEOFFSET) {
@@ -518,7 +406,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
uvscaleoff[2] = 0.0f;
uvscaleoff[3] = 0.0f;
}
glUniform4fv(u_uvscaleoffset, 1, uvscaleoff);
render_->SetUniformF(&u_uvscaleoffset, 4, uvscaleoff);
}
if ((dirty & DIRTY_TEXCLAMP) && u_texclamp != -1) {
@@ -540,21 +428,21 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
gstate_c.curTextureXOffset * invW,
gstate_c.curTextureYOffset * invH,
};
glUniform4fv(u_texclamp, 1, texclamp);
render_->SetUniformF(&u_texclamp, 4, texclamp);
if (u_texclampoff != -1) {
glUniform2fv(u_texclampoff, 1, texclampoff);
render_->SetUniformF(&u_texclampoff, 2, texclampoff);
}
}
// Transform
if (dirty & DIRTY_WORLDMATRIX) {
SetMatrix4x3(u_world, gstate.worldMatrix);
SetMatrix4x3(render_, &u_world, gstate.worldMatrix);
}
if (dirty & DIRTY_VIEWMATRIX) {
SetMatrix4x3(u_view, gstate.viewMatrix);
SetMatrix4x3(render_, &u_view, gstate.viewMatrix);
}
if (dirty & DIRTY_TEXMATRIX) {
SetMatrix4x3(u_texmtx, gstate.tgenMatrix);
SetMatrix4x3(render_, &u_texmtx, gstate.tgenMatrix);
}
if ((dirty & DIRTY_DEPTHRANGE) && u_depthRange != -1) {
// Since depth is [-1, 1] mapping to [minz, maxz], this is easyish.
@@ -580,53 +468,27 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
}
float data[4] = { viewZScale, viewZCenter, viewZCenter, viewZInvScale };
SetFloatUniform4(u_depthRange, data);
SetFloatUniform4(render_, &u_depthRange, data);
}
if (dirty & DIRTY_STENCILREPLACEVALUE) {
glUniform1f(u_stencilReplaceValue, (float)gstate.getStencilTestRef() * (1.0f / 255.0f));
float f = (float)gstate.getStencilTestRef() * (1.0f / 255.0f);
render_->SetUniformF(&u_stencilReplaceValue, 1, &f);
}
// TODO: Could even set all bones in one go if they're all dirty.
#ifdef USE_BONE_ARRAY
if (u_bone != -1) {
float allBones[8 * 16];
bool allDirty = true;
for (int i = 0; i < numBones; i++) {
if (dirty & (DIRTY_BONEMATRIX0 << i)) {
ConvertMatrix4x3To4x4(allBones + 16 * i, gstate.boneMatrix + 12 * i);
} else {
allDirty = false;
}
}
if (allDirty) {
// Set them all with one call
glUniformMatrix4fv(u_bone, numBones, GL_FALSE, allBones);
} else {
// Set them one by one. Could try to coalesce two in a row etc but too lazy.
for (int i = 0; i < numBones; i++) {
if (dirty & (DIRTY_BONEMATRIX0 << i)) {
glUniformMatrix4fv(u_bone + i, 1, GL_FALSE, allBones + 16 * i);
}
}
}
}
#else
float bonetemp[16];
for (int i = 0; i < numBones; i++) {
if (dirty & (DIRTY_BONEMATRIX0 << i)) {
ConvertMatrix4x3To4x4(bonetemp, gstate.boneMatrix + 12 * i);
glUniformMatrix4fv(u_bone[i], 1, GL_FALSE, bonetemp);
render_->SetUniformM4x4(&u_bone[i], bonetemp);
}
}
#endif
if (dirty & DIRTY_SHADERBLEND) {
if (u_blendFixA != -1) {
SetColorUniform3(u_blendFixA, gstate.getFixA());
SetColorUniform3(render_, &u_blendFixA, gstate.getFixA());
}
if (u_blendFixB != -1) {
SetColorUniform3(u_blendFixB, gstate.getFixB());
SetColorUniform3(render_, &u_blendFixB, gstate.getFixB());
}
const float fbotexSize[2] = {
@@ -634,25 +496,25 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
1.0f / (float)gstate_c.curRTRenderHeight,
};
if (u_fbotexSize != -1) {
glUniform2fv(u_fbotexSize, 1, fbotexSize);
render_->SetUniformF(&u_fbotexSize, 2, fbotexSize);
}
}
// Lighting
if (dirty & DIRTY_AMBIENT) {
SetColorUniform3Alpha(u_ambient, gstate.ambientcolor, gstate.getAmbientA());
SetColorUniform3Alpha(render_, &u_ambient, gstate.ambientcolor, gstate.getAmbientA());
}
if (dirty & DIRTY_MATAMBIENTALPHA) {
SetColorUniform3Alpha(u_matambientalpha, gstate.materialambient, gstate.getMaterialAmbientA());
SetColorUniform3Alpha(render_, &u_matambientalpha, gstate.materialambient, gstate.getMaterialAmbientA());
}
if (dirty & DIRTY_MATDIFFUSE) {
SetColorUniform3(u_matdiffuse, gstate.materialdiffuse);
SetColorUniform3(render_, &u_matdiffuse, gstate.materialdiffuse);
}
if (dirty & DIRTY_MATEMISSIVE) {
SetColorUniform3(u_matemissive, gstate.materialemissive);
SetColorUniform3(render_, &u_matemissive, gstate.materialemissive);
}
if (dirty & DIRTY_MATSPECULAR) {
SetColorUniform3ExtraFloat(u_matspecular, gstate.materialspecular, getFloat24(gstate.materialspecularcoef));
SetColorUniform3ExtraFloat(render_, &u_matspecular, gstate.materialspecular, getFloat24(gstate.materialspecularcoef));
}
for (int i = 0; i < 4; i++) {
@@ -668,34 +530,33 @@ void LinkedShader::UpdateUniforms(u32 vertType, const VShaderID &vsid) {
else
len = 1.0f / len;
float vec[3] = { x * len, y * len, z * len };
glUniform3fv(u_lightpos[i], 1, vec);
render_->SetUniformF(&u_lightpos[i], 3, vec);
} else {
SetFloat24Uniform3(u_lightpos[i], &gstate.lpos[i * 3]);
SetFloat24Uniform3(render_, &u_lightpos[i], &gstate.lpos[i * 3]);
}
if (u_lightdir[i] != -1) SetFloat24Uniform3(u_lightdir[i], &gstate.ldir[i * 3]);
if (u_lightatt[i] != -1) SetFloat24Uniform3(u_lightatt[i], &gstate.latt[i * 3]);
if (u_lightangle[i] != -1) glUniform1f(u_lightangle[i], getFloat24(gstate.lcutoff[i]));
if (u_lightspotCoef[i] != -1) glUniform1f(u_lightspotCoef[i], getFloat24(gstate.lconv[i]));
if (u_lightambient[i] != -1) SetColorUniform3(u_lightambient[i], gstate.lcolor[i * 3]);
if (u_lightdiffuse[i] != -1) SetColorUniform3(u_lightdiffuse[i], gstate.lcolor[i * 3 + 1]);
if (u_lightspecular[i] != -1) SetColorUniform3(u_lightspecular[i], gstate.lcolor[i * 3 + 2]);
if (u_lightdir[i] != -1) SetFloat24Uniform3(render_, &u_lightdir[i], &gstate.ldir[i * 3]);
if (u_lightatt[i] != -1) SetFloat24Uniform3(render_, &u_lightatt[i], &gstate.latt[i * 3]);
if (u_lightangle[i] != -1) SetFloatUniform(render_, &u_lightangle[i], getFloat24(gstate.lcutoff[i]));
if (u_lightspotCoef[i] != -1) SetFloatUniform(render_, &u_lightspotCoef[i], getFloat24(gstate.lconv[i]));
if (u_lightambient[i] != -1) SetColorUniform3(render_, &u_lightambient[i], gstate.lcolor[i * 3]);
if (u_lightdiffuse[i] != -1) SetColorUniform3(render_, &u_lightdiffuse[i], gstate.lcolor[i * 3 + 1]);
if (u_lightspecular[i] != -1) SetColorUniform3(render_, &u_lightspecular[i], gstate.lcolor[i * 3 + 2]);
}
}
if (dirty & DIRTY_BEZIERSPLINE) {
glUniform1i(u_spline_count_u, gstate_c.spline_count_u);
render_->SetUniformI1(&u_spline_count_u, gstate_c.spline_count_u);
if (u_spline_count_v != -1)
glUniform1i(u_spline_count_v, gstate_c.spline_count_v);
render_->SetUniformI1(&u_spline_count_v, gstate_c.spline_count_v);
if (u_spline_type_u != -1)
glUniform1i(u_spline_type_u, gstate_c.spline_type_u);
render_->SetUniformI1(&u_spline_type_u, gstate_c.spline_type_u);
if (u_spline_type_v != -1)
glUniform1i(u_spline_type_v, gstate_c.spline_type_v);
render_->SetUniformI1(&u_spline_type_v, gstate_c.spline_type_v);
}
CHECK_GL_ERROR_IF_DEBUG();
}
ShaderManagerGLES::ShaderManagerGLES()
: lastShader_(nullptr), shaderSwitchDirtyUniforms_(0), diskCacheDirty_(false), fsCache_(16), vsCache_(16) {
ShaderManagerGLES::ShaderManagerGLES(GLRenderManager *render)
: render_(render), lastShader_(nullptr), shaderSwitchDirtyUniforms_(0), diskCacheDirty_(false), fsCache_(16), vsCache_(16) {
codeBuffer_ = new char[16384];
lastFSID_.set_invalid();
lastVSID_.set_invalid();
@@ -737,8 +598,6 @@ void ShaderManagerGLES::DirtyShader() {
}
void ShaderManagerGLES::DirtyLastShader() { // disables vertex arrays
if (lastShader_)
lastShader_->stop();
lastShader_ = nullptr;
lastVShaderSame_ = false;
}
@@ -748,7 +607,8 @@ Shader *ShaderManagerGLES::CompileFragmentShader(FShaderID FSID) {
if (!GenerateFragmentShader(FSID, codeBuffer_, &uniformMask)) {
return nullptr;
}
return new Shader(FSID, codeBuffer_, GL_FRAGMENT_SHADER, false, 0, uniformMask);
std::string desc = FragmentShaderDesc(FSID);
return new Shader(render_, codeBuffer_, desc, GL_FRAGMENT_SHADER, false, 0, uniformMask);
}
Shader *ShaderManagerGLES::CompileVertexShader(VShaderID VSID) {
@@ -756,7 +616,8 @@ Shader *ShaderManagerGLES::CompileVertexShader(VShaderID VSID) {
uint32_t attrMask;
uint64_t uniformMask;
GenerateVertexShader(VSID, codeBuffer_, &attrMask, &uniformMask);
return new Shader(VSID, codeBuffer_, GL_VERTEX_SHADER, useHWTransform, attrMask, uniformMask);
std::string desc = VertexShaderDesc(VSID);
return new Shader(render_, codeBuffer_, desc, GL_VERTEX_SHADER, useHWTransform, attrMask, uniformMask);
}
Shader *ShaderManagerGLES::ApplyVertexShader(int prim, u32 vertType, VShaderID *VSID) {
@@ -806,7 +667,7 @@ Shader *ShaderManagerGLES::ApplyVertexShader(int prim, u32 vertType, VShaderID *
uint32_t attrMask;
uint64_t uniformMask;
GenerateVertexShader(vsidTemp, codeBuffer_, &attrMask, &uniformMask);
vs = new Shader(vsidTemp, codeBuffer_, GL_VERTEX_SHADER, false, attrMask, uniformMask);
vs = new Shader(render_, codeBuffer_, VertexShaderDesc(vsidTemp), GL_VERTEX_SHADER, false, attrMask, uniformMask);
}
vsCache_.Insert(*VSID, vs);
@@ -860,12 +721,12 @@ LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs,
_dbg_assert_(G3D, FSID.Bit(FS_BIT_FLATSHADE) == VSID.Bit(VS_BIT_FLATSHADE));
// Check if we can link these.
ls = new LinkedShader(VSID, vs, FSID, fs, vs->UseHWTransform());
ls->use(VSID, lastShader_);
ls = new LinkedShader(render_, VSID, vs, FSID, fs, vs->UseHWTransform());
ls->use(VSID);
const LinkedShaderCacheEntry entry(vs, fs, ls);
linkedShaderCache_.push_back(entry);
} else {
ls->use(VSID, lastShader_);
ls->use(VSID);
}
ls->UpdateUniforms(vertType, VSID);
@@ -1083,7 +944,7 @@ bool ShaderManagerGLES::ContinuePrecompile(float sliceTime) {
Shader *vs = vsCache_.Get(vsid);
Shader *fs = fsCache_.Get(fsid);
if (vs && fs) {
LinkedShader *ls = new LinkedShader(vsid, vs, fsid, fs, vs->UseHWTransform(), true);
LinkedShader *ls = new LinkedShader(render_, vsid, vs, fsid, fs, vs->UseHWTransform(), true);
LinkedShaderCacheEntry entry(vs, fs, ls);
linkedShaderCache_.push_back(entry);
}
+12 -9
View File
@@ -21,6 +21,7 @@
#include "base/basictypes.h"
#include "Common/Hashmaps.h"
#include "thin3d/GLRenderManager.h"
#include "GPU/Common/ShaderCommon.h"
#include "GPU/Common/ShaderId.h"
#include "GPU/GLES/VertexShaderGeneratorGLES.h"
@@ -43,18 +44,18 @@ enum {
class LinkedShader {
public:
LinkedShader(VShaderID VSID, Shader *vs, FShaderID FSID, Shader *fs, bool useHWTransform, bool preloading = false);
LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, FShaderID FSID, Shader *fs, bool useHWTransform, bool preloading = false);
~LinkedShader();
void use(const VShaderID &VSID, LinkedShader *previous);
void stop();
void UpdateUniforms(u32 vertType, const VShaderID &VSID);
void use(const ShaderID &VSID);
void UpdateUniforms(u32 vertType, const ShaderID &VSID);
GLRenderManager *render_;
Shader *vs_;
// Set to false if the VS failed, happens on Mali-400 a lot for complex shaders.
bool useHWTransform_;
uint32_t program = 0;
GLRProgram *program;
uint64_t availableUniforms;
uint64_t dirtyUniforms = 0;
@@ -124,12 +125,12 @@ public:
class Shader {
public:
Shader(const ShaderID &id, const char *code, uint32_t glShaderType, bool useHWTransform, uint32_t attrMask, uint64_t uniformMask);
Shader(GLRenderManager *render, const char *code, const std::string &desc, uint32_t glShaderType, bool useHWTransform, uint32_t attrMask, uint64_t uniformMask);
~Shader();
uint32_t shader;
GLRShader *shader;
bool Failed() const { return failed_; }
bool UseHWTransform() const { return useHWTransform_; } // only relevant for vtx shaders
bool UseHWTransform() const { return useHWTransform_; } // only relevant for vtx shaders
std::string GetShaderString(DebugShaderStringType type, ShaderID id) const;
@@ -137,6 +138,7 @@ public:
uint64_t GetUniformMask() const { return uniformMask_; }
private:
GLRenderManager *render_;
std::string source_;
bool failed_;
bool useHWTransform_;
@@ -147,7 +149,7 @@ private:
class ShaderManagerGLES : public ShaderManagerCommon {
public:
ShaderManagerGLES();
ShaderManagerGLES(GLRenderManager *render);
~ShaderManagerGLES();
void ClearCache(bool deleteThem); // TODO: deleteThem currently not respected
@@ -186,6 +188,7 @@ private:
};
typedef std::vector<LinkedShaderCacheEntry> LinkedShaderCache;
GLRenderManager *render_;
LinkedShaderCache linkedShaderCache_;
bool lastVShaderSame_;
+58 -122
View File
@@ -23,6 +23,7 @@
#include "StateMappingGLES.h"
#include "profiler/profiler.h"
#include "gfx/gl_debug_log.h"
#include "thin3d/GLRenderManager.h"
#include "GPU/Math3D.h"
#include "GPU/GPUState.h"
@@ -31,7 +32,6 @@
#include "Core/Config.h"
#include "Core/Reporting.h"
#include "GPU/GLES/GPU_GLES.h"
#include "ext/native/gfx/GLStateCache.h"
#include "GPU/GLES/ShaderManagerGLES.h"
#include "GPU/GLES/TextureCacheGLES.h"
#include "GPU/GLES/FramebufferManagerGLES.h"
@@ -123,18 +123,14 @@ static const GLushort logicOps[] = {
inline void DrawEngineGLES::ResetShaderBlending() {
if (fboTexBound_) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
renderManager->BindTexture(1, nullptr);
fboTexBound_ = false;
}
}
void DrawEngineGLES::ApplyDrawState(int prim) {
if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) {
textureCache_->SetTexture();
gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
}
GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
if (!gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE)) {
// Nothing to do, let's early-out
@@ -163,17 +159,10 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
gstate_c.SetAllowShaderBlend(!g_Config.bDisableSlowFramebufEffects);
if (gstate.isModeClear()) {
glstate.blend.disable();
// Color Test
bool colorMask = gstate.isClearModeColorMask();
bool alphaMask = gstate.isClearModeAlphaMask();
glstate.colorMask.set(colorMask, colorMask, colorMask, alphaMask);
#ifndef USING_GLES2
if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) {
// Logic Ops
glstate.colorLogicOp.disable();
}
#endif
renderManager->SetNoBlendAndMask((colorMask ? 7 : 0) | (alphaMask ? 8 : 0));
} else {
// Do the large chunks of state conversion. We might be able to hide these two behind a dirty-flag each,
// to avoid recomputing heavy stuff unnecessarily every draw call.
@@ -184,6 +173,21 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
if (ApplyShaderBlending()) {
// We may still want to do something about stencil -> alpha.
ApplyStencilReplaceAndLogicOp(blendState.replaceAlphaWithStencil, blendState);
// We copy the framebuffer here, as doing so will wipe any blend state if we do it later.
if (fboTexNeedBind_) {
// Note that this is positions, not UVs, that we need the copy from.
// TODO: If the device doesn't support blit, this will corrupt the currently applied texture.
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY);
// If we are rendering at a higher resolution, linear is probably best for the dest color.
renderManager->SetTextureSampler(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR, 0.0f);
fboTexBound_ = true;
fboTexNeedBind_ = false;
framebufferManager_->RebindFramebuffer();
// Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects.
gstate_c.Dirty(DIRTY_BLEND_STATE);
}
} else {
// Until next time, force it off.
ResetShaderBlending();
@@ -194,11 +198,6 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
}
if (blendState.enabled) {
glstate.blend.enable();
glstate.blendEquationSeparate.set(glBlendEqLookup[(size_t)blendState.eqColor], glBlendEqLookup[(size_t)blendState.eqAlpha]);
glstate.blendFuncSeparate.set(
glBlendFactorLookup[(size_t)blendState.srcColor], glBlendFactorLookup[(size_t)blendState.dstColor],
glBlendFactorLookup[(size_t)blendState.srcAlpha], glBlendFactorLookup[(size_t)blendState.dstAlpha]);
if (blendState.dirtyShaderBlend) {
gstate_c.Dirty(DIRTY_SHADERBLEND);
}
@@ -210,10 +209,8 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
(float)((color & 0xFF0000) >> 16) * (1.0f / 255.0f),
(float)((color & 0xFF000000) >> 24) * (1.0f / 255.0f),
};
glstate.blendColor.set(col);
renderManager->SetBlendFactor(col);
}
} else {
glstate.blend.disable();
}
// PSP color/alpha mask is per bit but we can only support per byte.
@@ -235,19 +232,20 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
WARN_LOG_REPORT_ONCE(amask, G3D, "Unsupported alpha/stencil mask: %02x", abits);
}
#endif
glstate.colorMask.set(rmask, gmask, bmask, amask);
int mask = (int)rmask | ((int)gmask << 1) | ((int)bmask << 2) | ((int)amask << 3);
if (blendState.enabled) {
renderManager->SetBlendAndMask(mask, blendState.enabled,
glBlendFactorLookup[(size_t)blendState.srcColor], glBlendFactorLookup[(size_t)blendState.dstColor],
glBlendFactorLookup[(size_t)blendState.srcAlpha], glBlendFactorLookup[(size_t)blendState.dstAlpha],
glBlendEqLookup[(size_t)blendState.eqColor], glBlendEqLookup[(size_t)blendState.eqAlpha]);
} else {
renderManager->SetNoBlendAndMask(mask);
}
#ifndef USING_GLES2
if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) {
// TODO: Make this dynamic
// Logic Ops
if (gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY) {
glstate.colorLogicOp.enable();
glstate.logicOp.set(logicOps[gstate.getLogicOp()]);
} else {
glstate.colorLogicOp.disable();
}
renderManager->SetLogicOp(gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY,
logicOps[gstate.getLogicOp()]);
}
#endif
}
@@ -257,26 +255,18 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
gstate_c.Clean(DIRTY_RASTER_STATE);
// Dither
if (gstate.isDitherEnabled()) {
glstate.dither.enable();
glstate.dither.set(true);
} else {
glstate.dither.disable();
}
bool dither = gstate.isDitherEnabled();
bool cullEnable;
GLenum cullMode = cullingMode[gstate.getCullMode() ^ !useBufferedRendering];
if (gstate.isModeClear()) {
// Culling
glstate.cullFace.disable();
cullEnable = false;
} else {
// Set cull
bool cullEnabled = !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES && gstate.isCullEnabled();
if (cullEnabled) {
glstate.cullFace.enable();
glstate.cullFaceMode.set(cullingMode[gstate.getCullMode() ^ !useBufferedRendering]);
} else {
glstate.cullFace.disable();
}
cullEnable = !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES && gstate.isCullEnabled();
}
renderManager->SetRaster(cullEnable, GL_CCW, cullMode, dither);
}
if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) {
@@ -284,50 +274,27 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
bool enableStencilTest = !g_Config.bDisableStencilTest;
if (gstate.isModeClear()) {
// Depth Test
glstate.depthTest.enable();
glstate.depthFunc.set(GL_ALWAYS);
glstate.depthWrite.set(gstate.isClearModeDepthMask() ? GL_TRUE : GL_FALSE);
if (gstate.isClearModeDepthMask()) {
framebufferManager_->SetDepthUpdated();
}
// Stencil Test
if (gstate.isClearModeAlphaMask() && enableStencilTest) {
glstate.stencilTest.enable();
glstate.stencilOp.set(GL_REPLACE, GL_REPLACE, GL_REPLACE);
// TODO: In clear mode, the stencil value is set to the alpha value of the vertex.
// A normal clear will be 2 points, the second point has the color.
// We should set "ref" to that value instead of 0.
// In case of clear rectangles, we set it again once we know what the color is.
glstate.stencilFunc.set(GL_ALWAYS, 255, 0xFF);
glstate.stencilMask.set(0xFF);
} else {
glstate.stencilTest.disable();
}
renderManager->SetStencilFunc(gstate.isClearModeAlphaMask() && enableStencilTest, GL_ALWAYS, 0xFF, 0xFF);
renderManager->SetStencilOp(0xFF, GL_REPLACE, GL_REPLACE, GL_REPLACE);
renderManager->SetDepth(true, gstate.isClearModeDepthMask() ? true : false, GL_ALWAYS);
} else {
// Depth Test
if (gstate.isDepthTestEnabled()) {
glstate.depthTest.enable();
glstate.depthFunc.set(compareOps[gstate.getDepthTestFunction()]);
glstate.depthWrite.set(gstate.isDepthWriteEnabled() ? GL_TRUE : GL_FALSE);
if (gstate.isDepthWriteEnabled()) {
framebufferManager_->SetDepthUpdated();
}
} else {
glstate.depthTest.disable();
renderManager->SetDepth(gstate.isDepthTestEnabled(), gstate.isDepthWriteEnabled(), compareOps[gstate.getDepthTestFunction()]);
if (gstate.isDepthTestEnabled() && gstate.isDepthWriteEnabled()) {
framebufferManager_->SetDepthUpdated();
}
GenericStencilFuncState stencilState;
ConvertStencilFuncState(stencilState);
// Stencil Test
if (stencilState.enabled) {
glstate.stencilTest.enable();
glstate.stencilFunc.set(compareOps[stencilState.testFunc], stencilState.testRef, stencilState.testMask);
glstate.stencilOp.set(stencilOps[stencilState.sFail], stencilOps[stencilState.zFail], stencilOps[stencilState.zPass]);
glstate.stencilMask.set(stencilState.writeMask);
renderManager->SetStencilFunc(stencilState.enabled, compareOps[stencilState.testFunc], stencilState.testRef, stencilState.testMask);
renderManager->SetStencilOp(stencilState.writeMask, stencilOps[stencilState.sFail], stencilOps[stencilState.zFail], stencilOps[stencilState.zPass]);
} else {
glstate.stencilTest.disable();
renderManager->SetStencilDisabled();
}
}
}
@@ -340,21 +307,11 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
framebufferManager_->GetTargetBufferWidth(), framebufferManager_->GetTargetBufferHeight(),
vpAndScissor);
if (vpAndScissor.scissorEnable) {
glstate.scissorTest.enable();
if (!useBufferedRendering) {
vpAndScissor.scissorY = PSP_CoreParameter().pixelHeight - vpAndScissor.scissorH - vpAndScissor.scissorY;
}
glstate.scissorRect.set(vpAndScissor.scissorX, vpAndScissor.scissorY, vpAndScissor.scissorW, vpAndScissor.scissorH);
} else {
glstate.scissorTest.disable();
}
if (!useBufferedRendering) {
vpAndScissor.viewportY = PSP_CoreParameter().pixelHeight - vpAndScissor.viewportH - vpAndScissor.viewportY;
}
glstate.viewport.set(vpAndScissor.viewportX, vpAndScissor.viewportY, vpAndScissor.viewportW, vpAndScissor.viewportH);
glstate.depthRange.set(vpAndScissor.depthRangeMin, vpAndScissor.depthRangeMax);
renderManager->SetScissor(GLRect2D{ vpAndScissor.scissorX, vpAndScissor.scissorY, vpAndScissor.scissorW, vpAndScissor.scissorH });
renderManager->SetViewport({
vpAndScissor.viewportX, vpAndScissor.viewportY,
vpAndScissor.viewportW, vpAndScissor.viewportH,
vpAndScissor.depthRangeMin, vpAndScissor.depthRangeMax });
if (vpAndScissor.dirtyProj) {
gstate_c.Dirty(DIRTY_PROJMATRIX);
@@ -363,40 +320,19 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
gstate_c.Dirty(DIRTY_DEPTHRANGE);
}
}
CHECK_GL_ERROR_IF_DEBUG();
}
void DrawEngineGLES::ApplyDrawStateLate() {
void DrawEngineGLES::ApplyDrawStateLate(bool setStencil, int stencilValue) {
if (setStencil) {
render_->SetStencilFunc(GL_TRUE, GL_ALWAYS, stencilValue, 255);
}
// At this point, we know if the vertices are full alpha or not.
// TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)?
if (!gstate.isModeClear()) {
if (fboTexNeedBind_) {
CHECK_GL_ERROR_IF_DEBUG();
// Note that this is positions, not UVs, that we need the copy from.
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY);
framebufferManager_->RebindFramebuffer();
CHECK_GL_ERROR_IF_DEBUG();
glActiveTexture(GL_TEXTURE1);
// If we are rendering at a higher resolution, linear is probably best for the dest color.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glActiveTexture(GL_TEXTURE0);
fboTexBound_ = true;
fboTexNeedBind_ = false;
CHECK_GL_ERROR_IF_DEBUG();
}
CHECK_GL_ERROR_IF_DEBUG();
// Apply the texture after the FBO tex, since it might unbind the texture.
// TODO: Could use a separate texture unit to be safer?
textureCache_->ApplyTexture();
CHECK_GL_ERROR_IF_DEBUG();
// Apply last, once we know the alpha params of the texture.
if (gstate.isAlphaTestEnabled() || gstate.isColorTestEnabled()) {
fragmentTestCache_->BindTestTexture(GL_TEXTURE2);
fragmentTestCache_->BindTestTexture(2);
}
CHECK_GL_ERROR_IF_DEBUG();
}
}
+29 -33
View File
@@ -17,7 +17,6 @@
#include "gfx_es2/glsl_program.h"
#include "Core/Reporting.h"
#include "ext/native/gfx/GLStateCache.h"
#include "GPU/Common/StencilCommon.h"
#include "GPU/GLES/FramebufferManagerGLES.h"
#include "GPU/GLES/ShaderManagerGLES.h"
@@ -111,12 +110,10 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe
}
// Let's not bother with the shader if it's just zero.
glstate.scissorTest.disable();
glstate.colorMask.set(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glClearColor(0, 0, 0, 0);
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
if (dstBuffer->fbo) {
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR });
}
render_->Clear(0, 0, 0, GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT, 0x8);
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
return true;
}
@@ -126,26 +123,26 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe
static std::string vs_code, fs_code;
vs_code = ApplyGLSLPrelude(stencil_vs, GL_VERTEX_SHADER);
fs_code = ApplyGLSLPrelude(stencil_fs, GL_FRAGMENT_SHADER);
stencilUploadProgram_ = glsl_create_source(vs_code.c_str(), fs_code.c_str(), &errorString);
std::vector<GLRShader *> shaders;
shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vs_code, "stencil"));
shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fs_code, "stencil"));
std::vector<GLRProgram::UniformLocQuery> queries;
queries.push_back({ &u_stencilUploadTex, "tex" });
queries.push_back({ &u_stencilValue, "u_stencilValue" });
std::vector<GLRProgram::Initializer> inits;
inits.push_back({ &u_stencilUploadTex, 0, 0 });
stencilUploadProgram_ = render_->CreateProgram(shaders, {}, queries, inits, false);
for (auto iter : shaders) {
render_->DeleteShader(iter);
}
if (!stencilUploadProgram_) {
ERROR_LOG_REPORT(G3D, "Failed to compile stencilUploadProgram! This shouldn't happen.\n%s", errorString.c_str());
} else {
glsl_bind(stencilUploadProgram_);
}
GLint u_tex = glsl_uniform_loc(stencilUploadProgram_, "tex");
glUniform1i(u_tex, 0);
} else {
glsl_bind(stencilUploadProgram_);
}
shaderManagerGL_->DirtyLastShader();
DisableState();
glstate.colorMask.set(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glstate.stencilTest.enable();
glstate.stencilOp.set(GL_REPLACE, GL_REPLACE, GL_REPLACE);
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE);
bool useBlit = gstate_c.Supports(GPU_SUPPORTS_ARB_FRAMEBUFFER_BLIT | GPU_SUPPORTS_NV_FRAMEBUFFER_BLIT);
@@ -164,43 +161,42 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe
} else if (dstBuffer->fbo) {
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR });
}
glViewport(0, 0, w, h);
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE);
render_->SetViewport({ 0, 0, (float)w, (float)h, 0.0f, 1.0f });
float u1 = 1.0f;
float v1 = 1.0f;
MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1);
textureCacheGL_->ForgetLastTexture();
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
// We must bind the program after starting the render pass, and set the color mask after clearing.
render_->Clear(0, 0, 0, GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, 0x8);
render_->SetStencilFunc(GL_TRUE, GL_ALWAYS, 0xFF, 0xFF);
render_->BindProgram(stencilUploadProgram_);
render_->SetNoBlendAndMask(0x8);
glstate.stencilFunc.set(GL_ALWAYS, 0xFF, 0xFF);
GLint u_stencilValue = glsl_uniform_loc(stencilUploadProgram_, "u_stencilValue");
for (int i = 1; i < values; i += i) {
if (!(usedBits & i)) {
// It's already zero, let's skip it.
continue;
}
if (dstBuffer->format == GE_FORMAT_4444) {
glstate.stencilMask.set((i << 4) | i);
glUniform1f(u_stencilValue, i * (16.0f / 255.0f));
render_->SetStencilOp((i << 4) | i, GL_REPLACE, GL_REPLACE, GL_REPLACE);
render_->SetUniformF1(&u_stencilValue, i * (16.0f / 255.0f));
} else if (dstBuffer->format == GE_FORMAT_5551) {
glstate.stencilMask.set(0xFF);
glUniform1f(u_stencilValue, i * (128.0f / 255.0f));
render_->SetStencilOp(0xFF, GL_REPLACE, GL_REPLACE, GL_REPLACE);
render_->SetUniformF1(&u_stencilValue, i * (128.0f / 255.0f));
} else {
glstate.stencilMask.set(i);
glUniform1f(u_stencilValue, i * (1.0f / 255.0f));
render_->SetStencilOp(i, GL_REPLACE, GL_REPLACE, GL_REPLACE);
render_->SetUniformF1(&u_stencilValue, i * (1.0f / 255.0f));
}
DrawActiveTexture(0, 0, dstBuffer->width, dstBuffer->height, dstBuffer->bufferWidth, dstBuffer->bufferHeight, 0.0f, 0.0f, u1, v1, ROTATION_LOCKED_HORIZONTAL, DRAWTEX_NEAREST);
}
glstate.stencilMask.set(0xFF);
if (useBlit) {
draw_->BlitFramebuffer(blitFBO, 0, 0, w, h, dstBuffer->fbo, 0, 0, dstBuffer->renderWidth, dstBuffer->renderHeight, Draw::FB_STENCIL_BIT, Draw::FB_BLIT_NEAREST);
}
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
RebindFramebuffer();
return true;
}
+147 -260
View File
@@ -23,6 +23,7 @@
#include "i18n/i18n.h"
#include "math/math_util.h"
#include "profiler/profiler.h"
#include "thin3d/GLRenderManager.h"
#include "Common/ColorConv.h"
#include "Core/Config.h"
@@ -31,7 +32,6 @@
#include "Core/Reporting.h"
#include "GPU/ge_constants.h"
#include "GPU/GPUState.h"
#include "ext/native/gfx/GLStateCache.h"
#include "GPU/GLES/TextureCacheGLES.h"
#include "GPU/GLES/FramebufferManagerGLES.h"
#include "GPU/GLES/FragmentShaderGeneratorGLES.h"
@@ -44,24 +44,23 @@
#include <emmintrin.h>
#endif
#ifndef GL_UNPACK_ROW_LENGTH
#define GL_UNPACK_ROW_LENGTH 0x0CF2
#endif
#define TEXCACHE_NAME_CACHE_SIZE 16
TextureCacheGLES::TextureCacheGLES(Draw::DrawContext *draw)
: TextureCacheCommon(draw) {
timesInvalidatedAllThisFrame_ = 0;
lastBoundTexture = INVALID_TEX;
lastBoundTexture = nullptr;
render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyLevel);
SetupTextureDecoder();
nextTexture_ = nullptr;
std::vector<GLRInputLayout::Entry> entries;
entries.push_back({ 0, 3, GL_FLOAT, GL_FALSE, 20, 0 });
entries.push_back({ 1, 2, GL_FLOAT, GL_FALSE, 20, 12 });
shadeInputLayout_ = render_->CreateInputLayout(entries);
}
TextureCacheGLES::~TextureCacheGLES() {
render_->DeleteInputLayout(shadeInputLayout_);
Clear(true);
}
@@ -71,23 +70,17 @@ void TextureCacheGLES::SetFramebufferManager(FramebufferManagerGLES *fbManager)
}
void TextureCacheGLES::ReleaseTexture(TexCacheEntry *entry, bool delete_them) {
DEBUG_LOG(G3D, "Deleting texture %i", entry->textureName);
DEBUG_LOG(G3D, "Deleting texture %08x", entry->addr);
if (delete_them) {
if (entry->textureName != 0) {
glDeleteTextures(1, &entry->textureName);
if (entry->textureName) {
render_->DeleteTexture(entry->textureName);
}
}
entry->textureName = 0;
entry->textureName = nullptr;
}
void TextureCacheGLES::Clear(bool delete_them) {
TextureCacheCommon::Clear(delete_them);
if (delete_them) {
if (!nameCache_.empty()) {
glDeleteTextures((GLsizei)nameCache_.size(), &nameCache_[0]);
nameCache_.clear();
}
}
}
GLenum getClutDestFormat(GEPaletteFormat format) {
@@ -122,7 +115,6 @@ static const GLuint MagFiltGL[2] = {
// This should not have to be done per texture! OpenGL is silly yo
void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) {
CHECK_GL_ERROR_IF_DEBUG();
int minFilt;
int magFilt;
bool sClamp;
@@ -133,59 +125,37 @@ void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) {
GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, maxLevel, entry.addr, mode);
if (gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) {
float minLod = 0.0f;
float maxLod = 0.0f;
if (maxLevel != 0) {
// TODO: What about a swap of autoMip mode?
if (force || entry.lodBias != lodBias) {
if (true) {
if (mode == GE_TEXLEVEL_MODE_AUTO) {
#ifndef USING_GLES2
// Sigh, LOD_BIAS is not even in ES 3.0.. but we could do it in the shader via texture()...
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, lodBias);
#endif
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, (float)maxLevel);
minLod = 0.0f;
maxLod = (float)maxLevel;
} else if (mode == GE_TEXLEVEL_MODE_CONST) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, std::max(0.0f, std::min((float)maxLevel, lodBias)));
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, std::max(0.0f, std::min((float)maxLevel, lodBias)));
minLod = std::max(0.0f, std::min((float)maxLevel, lodBias));
maxLod = std::max(0.0f, std::min((float)maxLevel, lodBias));
} else { // mode == GE_TEXLEVEL_MODE_SLOPE) {
// It's incorrect to use the slope as a bias. Instead it should be passed
// into the shader directly as an explicit lod level, with the bias on top. For now, we just kill the
// lodBias in this mode, working around #9772.
#ifndef USING_GLES2
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f);
lodBias = 0.0f;
#endif
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, (float)maxLevel);
minLod = 0.0f;
maxLod = (float)maxLevel;
}
entry.lodBias = lodBias;
}
} else {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0.0f);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0.0f);
minLod = 0.0f;
maxLod = 0.0f;
}
render_->SetTextureLod(minLod, maxLod, lodBias);
}
if (force || entry.minFilt != minFilt) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, MinFiltGL[minFilt]);
entry.minFilt = minFilt;
}
if (force || entry.magFilt != magFilt) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, MagFiltGL[magFilt]);
entry.magFilt = magFilt;
}
if (entry.framebuffer) {
WARN_LOG_REPORT_ONCE(wrongFramebufAttach, G3D, "Framebuffer still attached in UpdateSamplingParams()?");
}
if (force || entry.sClamp != sClamp) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
entry.sClamp = sClamp;
}
if (force || entry.tClamp != tClamp) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
entry.tClamp = tClamp;
}
CHECK_GL_ERROR_IF_DEBUG();
float aniso = 0.0f;
render_->SetTextureSampler(sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, MagFiltGL[magFilt], MinFiltGL[minFilt], aniso);
}
void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight) {
@@ -199,8 +169,8 @@ void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferH
minFilt &= 1; // framebuffers can't mipmap.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, MinFiltGL[minFilt]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, MagFiltGL[magFilt]);
float aniso = 0.0f;
render_->SetTextureSampler(sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, MagFiltGL[magFilt], MinFiltGL[minFilt], aniso);
// Often the framebuffer will not match the texture size. We'll wrap/clamp in the shader in that case.
// This happens whether we have OES_texture_npot or not.
@@ -209,9 +179,6 @@ void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferH
if (w != bufferWidth || h != bufferHeight) {
return;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
}
static void ConvertColors(void *dstBuf, const void *srcBuf, GLuint dstFmt, int numPixels) {
@@ -240,6 +207,19 @@ void TextureCacheGLES::StartFrame() {
InvalidateLastTexture();
timesInvalidatedAllThisFrame_ = 0;
GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
if (!lowMemoryMode_ && renderManager->SawOutOfMemory()) {
lowMemoryMode_ = true;
decimationCounter_ = 0;
I18NCategory *err = GetI18NCategory("Error");
if (standardScaleFactor_ > 1) {
host->NotifyUserMessage(err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f);
} else {
host->NotifyUserMessage(err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f);
}
}
if (texelsScaledThisFrame_) {
// INFO_LOG(G3D, "Scaled %i texels", texelsScaledThisFrame_);
}
@@ -336,39 +316,26 @@ bool SetDebugTexture() {
#endif
void TextureCacheGLES::BindTexture(TexCacheEntry *entry) {
CHECK_GL_ERROR_IF_DEBUG();
if (entry->textureName != lastBoundTexture) {
glBindTexture(GL_TEXTURE_2D, entry->textureName);
render_->BindTexture(0, entry->textureName);
lastBoundTexture = entry->textureName;
}
CHECK_GL_ERROR_IF_DEBUG();
UpdateSamplingParams(*entry, false);
CHECK_GL_ERROR_IF_DEBUG();
}
void TextureCacheGLES::Unbind() {
glBindTexture(GL_TEXTURE_2D, 0);
render_->BindTexture(0, nullptr);
InvalidateLastTexture();
}
class TextureShaderApplier {
public:
struct Pos {
Pos(float x_, float y_, float z_) : x(x_), y(y_), z(z_) {
}
Pos() {
}
float x;
float y;
float z;
};
struct UV {
UV(float u_, float v_) : u(u_), v(v_) {
}
UV() {
}
float u;
float v;
};
@@ -411,66 +378,44 @@ public:
const float top = v1 * invHalfHeight - 1.0f;
const float bottom = v2 * invHalfHeight - 1.0f;
// Points are: BL, BR, TR, TL.
pos_[0] = Pos(left, bottom, -1.0f);
pos_[1] = Pos(right, bottom, -1.0f);
pos_[2] = Pos(right, top, -1.0f);
pos_[3] = Pos(left, top, -1.0f);
pos_[0] = Pos{ left, bottom, -1.0f };
pos_[1] = Pos{ right, bottom, -1.0f };
pos_[2] = Pos{ right, top, -1.0f };
pos_[3] = Pos{ left, top, -1.0f };
// And also the UVs, same order.
const float uvleft = u1 * invWidth;
const float uvright = u2 * invWidth;
const float uvtop = v1 * invHeight;
const float uvbottom = v2 * invHeight;
uv_[0] = UV(uvleft, uvbottom);
uv_[1] = UV(uvright, uvbottom);
uv_[2] = UV(uvright, uvtop);
uv_[3] = UV(uvleft, uvtop);
uv_[0] = UV{ uvleft, uvbottom };
uv_[1] = UV{ uvright, uvbottom };
uv_[2] = UV{ uvright, uvtop };
uv_[3] = UV{ uvleft, uvtop };
}
}
void Use(DrawEngineGLES *transformDraw) {
glUseProgram(shader_->program);
// Restore will rebind all of the state below.
if (gstate_c.Supports(GPU_SUPPORTS_VAO)) {
static const GLubyte indices[4] = { 0, 1, 3, 2 };
transformDraw->BindBuffer(pos_, sizeof(pos_), uv_, sizeof(uv_));
transformDraw->BindElementBuffer(indices, sizeof(indices));
} else {
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
void Use(GLRenderManager *render, DrawEngineGLES *transformDraw, GLRInputLayout *inputLayout) {
render->BindProgram(shader_->program);
struct SimpleVertex {
float pos[3];
float uv[2];
};
uint32_t bindOffset;
GLRBuffer *bindBuffer;
SimpleVertex *verts = (SimpleVertex *)transformDraw->GetPushVertexBuffer()->Push(sizeof(SimpleVertex) * 4, &bindOffset, &bindBuffer);
int order[4] = { 0 ,1, 3, 2 };
for (int i = 0; i < 4; i++) {
memcpy(verts[i].pos, &pos_[order[i]], sizeof(Pos));
memcpy(verts[i].uv, &uv_[order[i]], sizeof(UV));
}
glEnableVertexAttribArray(shader_->a_position);
glEnableVertexAttribArray(shader_->a_texcoord0);
render->BindVertexBuffer(inputLayout, bindBuffer, bindOffset);
}
void Shade() {
void Shade(GLRenderManager *render) {
static const GLubyte indices[4] = { 0, 1, 3, 2 };
glstate.blend.force(false);
glstate.colorMask.force(true, true, true, true);
glstate.scissorTest.force(false);
glstate.cullFace.force(false);
glstate.depthTest.force(false);
glstate.stencilTest.force(false);
#if !defined(USING_GLES2)
glstate.colorLogicOp.force(false);
#endif
glViewport(0, 0, renderW_, renderH_);
if (gstate_c.Supports(GPU_SUPPORTS_VAO)) {
glVertexAttribPointer(shader_->a_position, 3, GL_FLOAT, GL_FALSE, 12, 0);
glVertexAttribPointer(shader_->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, (void *)sizeof(pos_));
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, 0);
} else {
glVertexAttribPointer(shader_->a_position, 3, GL_FLOAT, GL_FALSE, 12, pos_);
glVertexAttribPointer(shader_->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, uv_);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);
}
glDisableVertexAttribArray(shader_->a_position);
glDisableVertexAttribArray(shader_->a_texcoord0);
glstate.Restore();
render->SetViewport(GLRViewport{ 0, 0, (float)renderW_, (float)renderH_, 0.0f, 1.0f });
render->Draw(GL_TRIANGLE_STRIP, 0, 4);
}
protected:
@@ -491,31 +436,27 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram
}
if (depal) {
const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat();
GLuint clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_);
GLRTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_);
Draw::Framebuffer *depalFBO = framebufferManagerGL_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888);
draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
shaderManager_->DirtyLastShader();
TextureShaderApplier shaderApply(depal, framebuffer->bufferWidth, framebuffer->bufferHeight, framebuffer->renderWidth, framebuffer->renderHeight);
shaderApply.ApplyBounds(gstate_c.vertBounds, gstate_c.curTextureXOffset, gstate_c.curTextureYOffset);
shaderApply.Use(drawEngine_);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, clutTexture);
glActiveTexture(GL_TEXTURE0);
shaderApply.Use(render_, drawEngine_, shadeInputLayout_);
framebufferManagerGL_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_SKIP_COPY);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
render_->BindTexture(3, clutTexture);
render_->SetTextureSampler(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f);
shaderApply.Shade();
shaderApply.Shade(render_);
draw_->BindFramebufferAsTexture(depalFBO, 0, Draw::FB_COLOR_BIT, 0);
const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16);
const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor;
TexCacheEntry::TexStatus alphaStatus = CheckAlpha(clutBuf_, getClutDestFormat(clutFormat), clutTotalColors, clutTotalColors, 1);
TexCacheEntry::TexStatus alphaStatus = CheckAlpha((const uint8_t *)clutBuf_, getClutDestFormat(clutFormat), clutTotalColors, clutTotalColors, 1);
gstate_c.SetTextureFullAlpha(alphaStatus == TexCacheEntry::STATUS_ALPHA_FULL);
} else {
entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE;
@@ -528,9 +469,10 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram
framebufferManagerGL_->RebindFramebuffer();
SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight);
CHECK_GL_ERROR_IF_DEBUG();
InvalidateLastTexture();
// Since we started/ended render passes, might need these.
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
}
ReplacedTextureFormat FromGLESFormat(GLenum fmt) {
@@ -555,6 +497,10 @@ GLenum ToGLESFormat(ReplacedTextureFormat fmt) {
void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImages) {
entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK;
// Never replace images in-place - there's no such thing, drivers have to fake it anyway, at least if
// the image has been in use within the last frame or two.
replaceImages = false;
// For the estimate, we assume cluts always point to 8888 for simplicity.
cacheSizeEstimate_ += EstimateTexMemoryUsage(entry);
@@ -566,7 +512,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag
// Always generate a texture name unless it's a framebuffer, we might need it if the texture is replaced later.
if (!replaceImages) {
if (!entry->textureName) {
entry->textureName = AllocTextureName();
entry->textureName = render_->CreateTexture(GL_TEXTURE_2D);
}
}
@@ -630,8 +576,8 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag
if (replaced.GetSize(0, w, h)) {
if (replaceImages) {
// Since we're replacing the texture, we can't replace the image inside.
glDeleteTextures(1, &entry->textureName);
entry->textureName = AllocTextureName();
render_->DeleteTexture(entry->textureName);
entry->textureName = render_->CreateTexture(GL_TEXTURE_2D);
replaceImages = false;
}
@@ -663,31 +609,9 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag
}
}
glBindTexture(GL_TEXTURE_2D, entry->textureName);
// glBindTexture(GL_TEXTURE_2D, entry->textureName);
lastBoundTexture = entry->textureName;
// Disabled this due to issue #6075: https://github.com/hrydgard/ppsspp/issues/6075
// This breaks Dangan Ronpa 2 with mipmapping enabled. Why? No idea, it shouldn't.
// glTexStorage2D probably has few benefits for us anyway.
if (false && gl_extensions.GLES3 && maxLevel > 0) {
// glTexStorage2D requires the use of sized formats.
GLenum actualFmt = replaced.Valid() ? ToGLESFormat(replaced.Format(0)) : dstFmt;
GLenum storageFmt = GL_RGBA8;
switch (actualFmt) {
case GL_UNSIGNED_BYTE: storageFmt = GL_RGBA8; break;
case GL_UNSIGNED_SHORT_5_6_5: storageFmt = GL_RGB565; break;
case GL_UNSIGNED_SHORT_4_4_4_4: storageFmt = GL_RGBA4; break;
case GL_UNSIGNED_SHORT_5_5_5_1: storageFmt = GL_RGB5_A1; break;
default:
ERROR_LOG(G3D, "Unknown dstfmt %i", (int)actualFmt);
break;
}
// TODO: This may cause bugs, since it hard-sets the texture w/h, and we might try to reuse it later with a different size.
glTexStorage2D(GL_TEXTURE_2D, maxLevel + 1, storageFmt, w * scaleFactor, h * scaleFactor);
// Make sure we don't use glTexImage2D after glTexStorage2D.
replaceImages = true;
}
// GLES2 doesn't have support for a "Max lod" which is critical as PSP games often
// don't specify mips all the way down. As a result, we either need to manually generate
// the bottom few levels or rely on OpenGL's autogen mipmaps instead, which might not
@@ -702,33 +626,35 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag
LoadTextureLevel(*entry, replaced, 0, replaceImages, scaleFactor, dstFmt);
// Mipmapping only enable when texture scaling disable
int texMaxLevel = 0;
bool genMips = false;
if (maxLevel > 0 && scaleFactor == 1) {
if (gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) {
if (badMipSizes) {
// WARN_LOG(G3D, "Bad mipmap for texture sized %dx%dx%d - autogenerating", w, h, (int)format);
if (canAutoGen) {
glGenerateMipmap(GL_TEXTURE_2D);
genMips = true;
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
texMaxLevel = 0;
maxLevel = 0;
}
} else {
for (int i = 1; i <= maxLevel; i++) {
LoadTextureLevel(*entry, replaced, i, replaceImages, scaleFactor, dstFmt);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, maxLevel);
texMaxLevel = maxLevel;
}
} else {
// Avoid PowerVR driver bug
if (canAutoGen && w > 1 && h > 1 && !(h > w && (gl_extensions.bugs & BUG_PVR_GENMIPMAP_HEIGHT_GREATER))) { // Really! only seems to fail if height > width
// NOTICE_LOG(G3D, "Generating mipmap for texture sized %dx%d%d", w, h, (int)format);
glGenerateMipmap(GL_TEXTURE_2D);
genMips = true;
} else {
maxLevel = 0;
}
}
} else if (gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
texMaxLevel = 0;
}
if (maxLevel == 0) {
@@ -740,32 +666,12 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag
entry->SetAlphaStatus(TexCacheEntry::TexStatus(replaced.AlphaStatus()));
}
if (gstate_c.Supports(GPU_SUPPORTS_ANISOTROPY)) {
int aniso = 1 << g_Config.iAnisotropyLevel;
float anisotropyLevel = (float) aniso > maxAnisotropyLevel ? maxAnisotropyLevel : (float) aniso;
if (anisotropyLevel > 1.0f) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropyLevel);
}
}
render_->FinalizeTexture(entry->textureName, texMaxLevel, genMips);
// This will rebind it, but that's okay.
// Need to actually bind it now - it might only have gotten bound in the init phase.
render_->BindTexture(0, entry->textureName);
UpdateSamplingParams(*entry, true);
//glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
CHECK_GL_ERROR_IF_DEBUG();
}
u32 TextureCacheGLES::AllocTextureName() {
if (nameCache_.empty()) {
nameCache_.resize(TEXCACHE_NAME_CACHE_SIZE);
glGenTextures(TEXCACHE_NAME_CACHE_SIZE, &nameCache_[0]);
}
u32 name = nameCache_.back();
nameCache_.pop_back();
return name;
}
GLenum TextureCacheGLES::GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const {
@@ -790,7 +696,7 @@ GLenum TextureCacheGLES::GetDestFormat(GETextureFormat format, GEPaletteFormat c
}
}
void *TextureCacheGLES::DecodeTextureLevelOld(GETextureFormat format, GEPaletteFormat clutformat, int level, GLenum dstFmt, int scaleFactor, int *bufwout) {
u8 *TextureCacheGLES::DecodeTextureLevelOld(GETextureFormat format, GEPaletteFormat clutformat, int level, GLenum dstFmt, int scaleFactor, int *bufwout) {
void *finalBuf = nullptr;
u32 texaddr = gstate.getTextureAddress(level);
int bufw = GetTextureBufw(level, texaddr, format);
@@ -808,26 +714,26 @@ void *TextureCacheGLES::DecodeTextureLevelOld(GETextureFormat format, GEPaletteF
decPitch = bufw * pixelSize;
}
tmpTexBufRearrange_.resize(std::max(w, bufw) * h);
DecodeTextureLevel((u8 *)tmpTexBufRearrange_.data(), decPitch, format, clutformat, texaddr, level, bufw, true, false, false);
return tmpTexBufRearrange_.data();
uint8_t *texBuf = new uint8_t[std::max(w, bufw) * h * pixelSize];
DecodeTextureLevel(texBuf, decPitch, format, clutformat, texaddr, level, bufw, true, false, false);
return texBuf;
}
TexCacheEntry::TexStatus TextureCacheGLES::CheckAlpha(const u32 *pixelData, GLenum dstFmt, int stride, int w, int h) {
TexCacheEntry::TexStatus TextureCacheGLES::CheckAlpha(const uint8_t *pixelData, GLenum dstFmt, int stride, int w, int h) {
CheckAlphaResult res;
switch (dstFmt) {
case GL_UNSIGNED_SHORT_4_4_4_4:
res = CheckAlphaABGR4444Basic(pixelData, stride, w, h);
res = CheckAlphaABGR4444Basic((const uint32_t *)pixelData, stride, w, h);
break;
case GL_UNSIGNED_SHORT_5_5_5_1:
res = CheckAlphaABGR1555Basic(pixelData, stride, w, h);
res = CheckAlphaABGR1555Basic((const uint32_t *)pixelData, stride, w, h);
break;
case GL_UNSIGNED_SHORT_5_6_5:
// Never has any alpha.
res = CHECKALPHA_FULL;
break;
default:
res = CheckAlphaRGBA8888Basic(pixelData, stride, w, h);
res = CheckAlphaRGBA8888Basic((const uint32_t *)pixelData, stride, w, h);
break;
}
@@ -838,9 +744,7 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
int w = gstate.getTextureWidth(level);
int h = gstate.getTextureHeight(level);
bool useUnpack = false;
u32 *pixelData;
CHECK_GL_ERROR_IF_DEBUG();
uint8_t *pixelData;
// TODO: only do this once
u32 texByteAlign = 1;
@@ -850,10 +754,10 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
if (replaced.GetSize(level, w, h)) {
PROFILE_THIS_SCOPE("replacetex");
tmpTexBufRearrange_.resize(w * h);
int bpp = replaced.Format(level) == ReplacedTextureFormat::F_8888 ? 4 : 2;
replaced.Load(level, tmpTexBufRearrange_.data(), bpp * w);
pixelData = tmpTexBufRearrange_.data();
uint8_t *rearrange = new uint8_t[w * h * bpp];
replaced.Load(level, rearrange, bpp * w);
pixelData = rearrange;
dstFmt = ToGLESFormat(replaced.Format(level));
@@ -863,20 +767,14 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
GEPaletteFormat clutformat = gstate.getClutPaletteFormat();
int bufw;
void *finalBuf = DecodeTextureLevelOld(GETextureFormat(entry.format), clutformat, level, dstFmt, scaleFactor, &bufw);
if (finalBuf == NULL) {
uint8_t *finalBuf = DecodeTextureLevelOld(GETextureFormat(entry.format), clutformat, level, dstFmt, scaleFactor, &bufw);
if (!finalBuf) {
return;
}
// Can restore these and remove the fixup at the end of DecodeTextureLevel on desktop GL and GLES 3.
if (scaleFactor == 1 && gstate_c.Supports(GPU_SUPPORTS_UNPACK_SUBIMAGE) && w != bufw) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, bufw);
useUnpack = true;
}
// Textures are always aligned to 16 bytes bufw, so this could safely be 4 always.
texByteAlign = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2;
pixelData = (u32 *)finalBuf;
pixelData = finalBuf;
// We check before scaling since scaling shouldn't invent alpha from a full alpha texture.
if ((entry.status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) {
@@ -886,8 +784,12 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
entry.SetAlphaStatus(TexCacheEntry::STATUS_ALPHA_UNKNOWN);
}
if (scaleFactor > 1)
scaler.Scale(pixelData, dstFmt, w, h, scaleFactor);
if (scaleFactor > 1) {
uint8_t *rearrange = new uint8_t[w * scaleFactor * h * scaleFactor * 4];
scaler.ScaleAlways((u32 *)rearrange, (u32 *)pixelData, dstFmt, w, h, scaleFactor);
pixelData = rearrange;
delete [] finalBuf;
}
if (replacer_.Enabled()) {
ReplacedTextureDecodeInfo replacedInfo;
@@ -904,56 +806,20 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
}
}
glPixelStorei(GL_UNPACK_ALIGNMENT, texByteAlign);
CHECK_GL_ERROR_IF_DEBUG();
GLuint components = dstFmt == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA;
GLuint components2 = components;
if (replaceImages) {
PROFILE_THIS_SCOPE("repltex");
glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, w, h, components2, dstFmt, pixelData);
Crash();
// glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, w, h, components2, dstFmt, pixelData);
} else {
PROFILE_THIS_SCOPE("loadtex");
// Avoid misleading errors in texture upload, these are common.
GLenum err = glGetError();
if (err) {
WARN_LOG(G3D, "Got an error BEFORE texture upload: %08x (%s)", err, GLEnumToString(err).c_str());
}
if (IsFakeMipmapChange())
glTexImage2D(GL_TEXTURE_2D, 0, components, w, h, 0, components2, dstFmt, pixelData);
render_->TextureImage(entry.textureName, 0, w, h, components, components2, dstFmt, pixelData);
else
glTexImage2D(GL_TEXTURE_2D, level, components, w, h, 0, components2, dstFmt, pixelData);
if (!lowMemoryMode_) {
// TODO: We really, really should avoid calling glGetError.
GLenum err = glGetError();
if (err == GL_OUT_OF_MEMORY) {
WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode");
lowMemoryMode_ = true;
decimationCounter_ = 0;
Decimate();
// Try again, now that we've cleared out textures in lowMemoryMode_.
glTexImage2D(GL_TEXTURE_2D, level, components, w, h, 0, components2, dstFmt, pixelData);
I18NCategory *err = GetI18NCategory("Error");
if (scaleFactor > 1) {
host->NotifyUserMessage(err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f);
} else {
host->NotifyUserMessage(err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f);
}
} else if (err != GL_NO_ERROR) {
// We checked the err anyway, might as well log if there is one.
WARN_LOG(G3D, "Got an error in texture upload: %08x (%s) (components=%s components2=%s dstFmt=%s w=%d h=%d level=%d)",
err, GLEnumToString(err).c_str(), GLEnumToString(components).c_str(), GLEnumToString(components2).c_str(), GLEnumToString(dstFmt).c_str(),
w, h, level);
}
}
}
if (useUnpack) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
render_->TextureImage(entry.textureName, level, w, h, components, components2, dstFmt, pixelData);
}
}
@@ -1046,19 +912,40 @@ bool TextureCacheGLES::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level)
}
SetTexture(true);
if (!nextTexture_)
return false;
// Apply texture may need to rebuild the texture if we're about to render, or bind a framebuffer.
TexCacheEntry *entry = nextTexture_;
ApplyTexture();
// TODO: Centralize?
if (entry->framebuffer) {
VirtualFramebuffer *vfb = entry->framebuffer;
buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, false);
bool retval = draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, 0, vfb->bufferWidth, vfb->bufferHeight, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), vfb->bufferWidth);
// Vulkan requires us to re-apply all dynamic state for each command buffer, and the above will cause us to start a new cmdbuf.
// So let's dirty the things that are involved in Vulkan dynamic state. Readbacks are not frequent so this won't hurt other backends.
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE);
// We may have blitted to a temp FBO.
framebufferManager_->RebindFramebuffer();
return retval;
}
GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
// Not a framebuffer, so let's assume these are right.
int w = gstate.getTextureWidth(level);
int h = gstate.getTextureHeight(level);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
if (level != 0) {
gstate = saved;
}
buffer.Allocate(w, h, GE_FORMAT_8888, false);
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.GetData());
renderManager->CopyImageToMemorySync(entry->textureName, level, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, (uint8_t *)buffer.GetData(), w);
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
framebufferManager_->RebindFramebuffer();
return true;
#else
+13 -9
View File
@@ -21,6 +21,7 @@
#include "gfx_es2/gpu_features.h"
#include "gfx/gl_common.h"
#include "thin3d/GLRenderManager.h"
#include "GPU/GPUInterface.h"
#include "GPU/GPUState.h"
#include "GPU/GLES/TextureScalerGLES.h"
@@ -31,6 +32,7 @@ class FramebufferManagerGLES;
class DepalShaderCacheGLES;
class ShaderManagerGLES;
class DrawEngineGLES;
class GLRTexture;
class TextureCacheGLES : public TextureCacheCommon {
public:
@@ -52,17 +54,15 @@ public:
}
void ForgetLastTexture() override {
lastBoundTexture = INVALID_TEX;
lastBoundTexture = nullptr;
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
}
void InvalidateLastTexture(TexCacheEntry *entry = nullptr) override {
if (!entry || entry->textureName == lastBoundTexture) {
lastBoundTexture = INVALID_TEX;
lastBoundTexture = nullptr;
}
}
u32 AllocTextureName();
// Only used by Qt UI?
bool DecodeTexture(u8 *output, const GPUgstate &state);
@@ -80,25 +80,29 @@ private:
void UpdateSamplingParams(TexCacheEntry &entry, bool force);
void LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, bool replaceImages, int scaleFactor, GLenum dstFmt);
GLenum GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const;
void *DecodeTextureLevelOld(GETextureFormat format, GEPaletteFormat clutformat, int level, GLenum dstFmt, int scaleFactor, int *bufw = 0);
TexCacheEntry::TexStatus CheckAlpha(const u32 *pixelData, GLenum dstFmt, int stride, int w, int h);
// Caller owns the returned buffer. Delete[].
u8 *DecodeTextureLevelOld(GETextureFormat format, GEPaletteFormat clutformat, int level, GLenum dstFmt, int scaleFactor, int *bufw = 0);
TexCacheEntry::TexStatus CheckAlpha(const uint8_t *pixelData, GLenum dstFmt, int stride, int w, int h);
void UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple) override;
void ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) override;
void BuildTexture(TexCacheEntry *const entry, bool replaceImages) override;
std::vector<u32> nameCache_;
GLRenderManager *render_;
TextureScalerGLES scaler;
u32 lastBoundTexture;
float maxAnisotropyLevel;
GLRTexture *lastBoundTexture;
FramebufferManagerGLES *framebufferManagerGL_;
DepalShaderCacheGLES *depalShaderCache_;
ShaderManagerGLES *shaderManager_;
DrawEngineGLES *drawEngine_;
GLRInputLayout *shadeInputLayout_;
enum { INVALID_TEX = -1 };
};
+7 -12
View File
@@ -107,7 +107,6 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
const char *attribute = "attribute";
const char * const * boneWeightDecl = boneWeightAttrDecl;
const char *texelFetch = NULL;
bool isAllowTexture1D = false;
bool highpFog = false;
bool highpTexcoord = false;
@@ -135,13 +134,11 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
glslES30 = true;
WRITE(p, "#version 330\n");
texelFetch = "texelFetch";
isAllowTexture1D = true;
} else if (gl_extensions.VersionGEThan(3, 0, 0)) {
WRITE(p, "#version 130\n");
if (gl_extensions.EXT_gpu_shader4) {
WRITE(p, "#extension GL_EXT_gpu_shader4 : enable\n");
texelFetch = "texelFetch";
isAllowTexture1D = true;
}
} else {
WRITE(p, "#version 110\n");
@@ -374,10 +371,9 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
if (doBezier || doSpline) {
*uniformMask |= DIRTY_BEZIERSPLINE;
const char *sampler = !isAllowTexture1D ? "sampler2D" : "sampler1D";
WRITE(p, "uniform %s u_tess_pos_tex;\n", sampler);
WRITE(p, "uniform %s u_tess_tex_tex;\n", sampler);
WRITE(p, "uniform %s u_tess_col_tex;\n", sampler);
WRITE(p, "uniform sampler2D u_tess_pos_tex;\n");
WRITE(p, "uniform sampler2D u_tess_tex_tex;\n");
WRITE(p, "uniform sampler2D u_tess_col_tex;\n");
WRITE(p, "uniform int u_spline_count_u;\n");
@@ -502,12 +498,11 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
WRITE(p, " for (int i = 0; i < 4; i++) {\n");
WRITE(p, " for (int j = 0; j < 4; j++) {\n");
WRITE(p, " int index = (i + v%s) * u_spline_count_u + (j + u%s);\n", doBezier ? " * 3" : "", doBezier ? " * 3" : "");
const char *index = !isAllowTexture1D ? "ivec2(index, 0)" : "index";
WRITE(p, " _pos[i * 4 + j] = %s(u_tess_pos_tex, %s, 0).xyz;\n", texelFetch, index);
WRITE(p, " _pos[i * 4 + j] = %s(u_tess_pos_tex, ivec2(index, 0), 0).xyz;\n", texelFetch);
if (doTexture && hasTexcoord && hasTexcoordTess)
WRITE(p, " _tex[i * 4 + j] = %s(u_tess_tex_tex, %s, 0).xy;\n", texelFetch, index);
WRITE(p, " _tex[i * 4 + j] = %s(u_tess_tex_tex, ivec2(index, 0), 0).xy;\n", texelFetch);
if (hasColor && hasColorTess)
WRITE(p, " _col[i * 4 + j] = %s(u_tess_col_tex, %s, 0).rgba;\n", texelFetch, index);
WRITE(p, " _col[i * 4 + j] = %s(u_tess_col_tex, ivec2(index, 0), 0).rgba;\n", texelFetch);
WRITE(p, " }\n");
WRITE(p, " }\n");
WRITE(p, " vec2 tess_pos = position.xy;\n");
@@ -536,7 +531,7 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
if (hasColorTess)
WRITE(p, " vec4 col = tess_sample(_col, weights);\n");
else
WRITE(p, " vec4 col = %s(u_tess_col_tex, %s, 0).rgba;\n", texelFetch, !isAllowTexture1D ? "ivec2(0, 0)" : "0");
WRITE(p, " vec4 col = %s(u_tess_col_tex, ivec2(0, 0), 0).rgba;\n", texelFetch);
}
if (hasNormal) {
// Curved surface is probably always need to compute normal(not sampling from control points)
+8
View File
@@ -847,6 +847,10 @@ void DrawEngineVulkan::DoFlush() {
ApplyDrawStateLate(renderManager, false, 0, pipeline->useBlendConstant);
gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
lastPipeline_ = pipeline;
// Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects.
if (fboTexBound_)
gstate_c.Dirty(DIRTY_BLEND_STATE);
}
lastPrim_ = prim;
@@ -947,6 +951,10 @@ void DrawEngineVulkan::DoFlush() {
ApplyDrawStateLate(renderManager, result.setStencil, result.stencilValue, pipeline->useBlendConstant);
gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
lastPipeline_ = pipeline;
// Must dirty blend state here so we re-copy next time. Example: Lunar's spell effects.
if (fboTexBound_)
gstate_c.Dirty(DIRTY_BLEND_STATE);
}
lastPrim_ = prim;
+24 -24
View File
@@ -38,11 +38,15 @@ layout (location = 0) in vec2 v_texcoord0;
layout (location = 0) out vec4 fragColor0;
void main() {
vec4 index = texture(tex, v_texcoord0);
int indexBits = int(floor(index.a * 255.99)) & 0xFF;
if ((indexBits & u_stencilValue) == 0)
discard;
fragColor0 = index.aaaa;
if (u_stencilValue == 0) {
fragColor0 = vec4(0.0);
} else {
vec4 index = texture(tex, v_texcoord0);
int indexBits = int(floor(index.a * 255.99)) & 0xFF;
if ((indexBits & u_stencilValue) == 0)
discard;
fragColor0 = index.aaaa;
}
}
)";
@@ -116,18 +120,6 @@ bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skip
VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
if (usedBits == 0) {
if (skipZero) {
// Common when creating buffers, it's already 0. We're done.
return false;
}
// TODO: Find a nice way to clear alpha here too.
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR });
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
return true;
}
shaderManagerVulkan_->DirtyLastShader();
textureCacheVulkan_->ForgetLastTexture();
@@ -150,26 +142,34 @@ bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skip
VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(overrideImageView_, nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE);
// Note: Even with skipZero, we don't necessarily start framebuffers at 0 in Vulkan. Clear anyway.
// Not an actual clear, because we need to draw to alpha only as well.
uint32_t value = 0;
renderManager->PushConstants(vulkan2D_->GetPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT|VK_SHADER_STAGE_FRAGMENT_BIT, 0, 4, &value);
renderManager->SetStencilParams(0xFF, 0xFF, 0x00);
renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, VK_NULL_HANDLE, 0, 3); // full screen triangle
for (int i = 1; i < values; i += i) {
if (!(usedBits & i)) {
// It's already zero, let's skip it.
continue;
}
// These feel a little backwards : Mask is the bits that are going to be written, while value
// is the "mask" that will be tested against.
uint8_t mask = 0;
// These are the stencil bits that will be written. We discard when the bit doesn't match.
uint8_t writeMask = 0;
// This is the value to test the texture alpha against in the shader.
uint32_t value = 0;
if (dstBuffer->format == GE_FORMAT_4444) {
mask = i | (i << 4);
writeMask = i | (i << 4);
value = i * 16;
} else if (dstBuffer->format == GE_FORMAT_5551) {
mask = 0xFF;
writeMask = 0xFF;
value = i * 128;
} else {
mask = i;
writeMask = i;
value = i;
}
renderManager->SetStencilParams(mask, 0xFF, 0xFF);
renderManager->SetStencilParams(writeMask, 0xFF, 0xFF);
// Need to specify both VERTEX and FRAGMENT bits here since that's what we set up in the pipeline layout, and we need
// that for the post shaders. There's probably not really a cost to this.
renderManager->PushConstants(vulkan2D_->GetPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT|VK_SHADER_STAGE_FRAGMENT_BIT, 0, 4, &value);
+9 -7
View File
@@ -784,16 +784,18 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt
}
bool TextureCacheVulkan::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) {
ApplyTexture();
SetTexture(false);
if (!nextTexture_)
return false;
// Apply texture may need to rebuild the texture if we're about to render, or bind a framebuffer.
TexCacheEntry *entry = nextTexture_;
ApplyTexture();
// TODO: Centralize?
if (nextTexture_->framebuffer) {
VirtualFramebuffer *vfb = nextTexture_->framebuffer;
bool flipY = GetGPUBackend() == GPUBackend::OPENGL && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE;
buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, flipY);
if (entry->framebuffer) {
VirtualFramebuffer *vfb = entry->framebuffer;
buffer.Allocate(vfb->bufferWidth, vfb->bufferHeight, GPU_DBG_FORMAT_8888, false);
bool retval = draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, 0, vfb->bufferWidth, vfb->bufferHeight, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), vfb->bufferWidth);
// Vulkan requires us to re-apply all dynamic state for each command buffer, and the above will cause us to start a new cmdbuf.
// So let's dirty the things that are involved in Vulkan dynamic state. Readbacks are not frequent so this won't hurt other backends.
@@ -803,9 +805,9 @@ bool TextureCacheVulkan::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int leve
return retval;
}
if (!nextTexture_->vkTex || !nextTexture_->vkTex->texture_)
if (!entry->vkTex || !entry->vkTex->texture_)
return false;
VulkanTexture *texture = nextTexture_->vkTex->texture_;
VulkanTexture *texture = entry->vkTex->texture_;
VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
GPUDebugBufferFormat bufferFormat;
-1
View File
@@ -1,5 +1,4 @@
#include "debugger_memorytex.h"
#include "gfx/GLStateCache.h"
#include "gfx/gl_common.h"
#include "ui_debugger_memorytex.h"
#include "Core/MemMap.h"
+76 -21
View File
@@ -29,6 +29,7 @@
#include "QtMain.h"
#include "gfx_es2/gpu_features.h"
#include "math/math_util.h"
#include "thread/threadutil.h"
#include <string.h>
@@ -188,31 +189,64 @@ static int mainInternal(QApplication &a)
return a.exec();
}
void MainUI::EmuThreadFunc() {
ILOG("In emu thread");
setCurrentThreadName("Emu");
// There's no real requirement that NativeInit happen on this thread.
// We just call the update/render loop here.
emuThreadState = (int)EmuThreadState::RUNNING;
while (emuThreadState != (int)EmuThreadState::QUIT_REQUESTED) {
#ifdef SDL
SDL_PumpEvents();
#endif
updateAccelerometer();
time_update();
UpdateRunLoop();
}
emuThreadState = (int)EmuThreadState::STOPPED;
}
void MainUI::EmuThreadStart() {
emuThreadState = (int)EmuThreadState::START_REQUESTED;
emuThread = std::thread([&]() { this->EmuThreadFunc(); } );
}
void MainUI::EmuThreadStop() {
emuThreadState = (int)EmuThreadState::QUIT_REQUESTED;
emuThread.join();
emuThread = std::thread();
}
MainUI::MainUI(QWidget *parent):
QGLWidget(parent)
QGLWidget(parent)
{
setAttribute(Qt::WA_AcceptTouchEvents);
emuThreadState = (int)EmuThreadState::DISABLED;
setAttribute(Qt::WA_AcceptTouchEvents);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
setAttribute(Qt::WA_LockLandscapeOrientation);
setAttribute(Qt::WA_LockLandscapeOrientation);
#endif
#if defined(MOBILE_DEVICE)
acc = new QAccelerometer(this);
acc->start();
acc = new QAccelerometer(this);
acc->start();
#endif
setFocus();
setFocusPolicy(Qt::StrongFocus);
startTimer(16);
setFocus();
setFocusPolicy(Qt::StrongFocus);
startTimer(16);
}
MainUI::~MainUI()
{
if (useThread_) {
EmuThreadStop();
}
#if defined(MOBILE_DEVICE)
delete acc;
delete acc;
#endif
NativeShutdownGraphics();
graphicsContext->Shutdown();
delete graphicsContext;
graphicsContext = nullptr;
NativeShutdownGraphics();
graphicsContext->Shutdown();
delete graphicsContext;
graphicsContext = nullptr;
}
QString MainUI::InputBoxGetQString(QString title, QString defaultValue)
@@ -330,18 +364,36 @@ void MainUI::initializeGL()
if (gl_extensions.IsCoreContext)
glGetError();
#endif
ILOG("Initializing graphics context");
graphicsContext = new QtDummyGraphicsContext();
NativeInitGraphics(graphicsContext);
// OpenGL uses a background thread to do the main processing and only renders on the gl thread.
useThread_ = g_Config.iGPUBackend == (int)GPUBackend::OPENGL;
if (useThread_) {
ILOG("Using thread, starting emu thread");
EmuThreadStart();
graphicsContext->ThreadStart();
} else {
ILOG("Not using thread, backend=%d", (int)g_Config.iGPUBackend);
}
}
void MainUI::paintGL()
{
#ifdef SDL
SDL_PumpEvents();
#endif
updateAccelerometer();
time_update();
UpdateRunLoop();
if (useThread_) {
graphicsContext->ThreadFrame();
// Do the rest in EmuThreadFunc
} else {
#ifdef SDL
SDL_PumpEvents();
#endif
updateAccelerometer();
time_update();
UpdateRunLoop();
}
}
void MainUI::updateAccelerometer()
@@ -454,14 +506,17 @@ int main(int argc, char *argv[])
#endif
savegame_dir += "/";
external_dir += "/";
bool fullscreenCLI=false;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i],"--fullscreen"))
fullscreenCLI=true;
}
NativeInit(argc, (const char **)argv, savegame_dir.c_str(), external_dir.c_str(), nullptr, fullscreenCLI);
// TODO: Support other backends than GL, like Vulkan, in the Qt backend.
g_Config.iGPUBackend = (int)GPUBackend::OPENGL;
int ret = mainInternal(a);
NativeShutdownGraphics();
+41 -2
View File
@@ -19,6 +19,8 @@ QTM_USE_NAMESPACE
#endif
#include <cassert>
#include <atomic>
#include <thread>
#include "base/display.h"
#include "base/logging.h"
@@ -37,6 +39,7 @@ QTM_USE_NAMESPACE
#include "Core/Core.h"
#include "Core/Config.h"
#include "Core/System.h"
#include "thin3d/GLRenderManager.h"
// Input
void SimulateGamepad();
@@ -47,21 +50,48 @@ public:
CheckGLExtensions();
draw_ = Draw::T3DCreateGLContext();
SetGPUBackend(GPUBackend::OPENGL);
renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
bool success = draw_->CreatePresets();
assert(success);
}
~QtDummyGraphicsContext() {
delete draw_;
draw_ = nullptr;
renderManager_ = nullptr;
}
Draw::DrawContext *GetDrawContext() override {
return draw_;
}
void ThreadStart() override {
renderManager_->ThreadStart();
}
bool ThreadFrame() override {
return renderManager_->ThreadFrame();
}
void ThreadEnd() override {
renderManager_->ThreadEnd();
}
private:
Draw::DrawContext *draw_;
Draw::DrawContext *draw_ = nullptr;
GLRenderManager *renderManager_ = nullptr;
};
//GUI
enum class EmuThreadState {
DISABLED,
START_REQUESTED,
RUNNING,
QUIT_REQUESTED,
STOPPED,
};
// GUI, thread manager
class MainUI : public QGLWidget
{
Q_OBJECT
@@ -87,6 +117,10 @@ protected:
void updateAccelerometer();
void EmuThreadFunc();
void EmuThreadStart();
void EmuThreadStop();
private:
QtDummyGraphicsContext *graphicsContext;
@@ -94,6 +128,11 @@ private:
#if defined(MOBILE_DEVICE)
QAccelerometer* acc;
#endif
std::thread emuThread;
std::atomic<int> emuThreadState;
bool useThread_ = false;
};
extern MainUI* emugl;
+85 -6
View File
@@ -14,9 +14,11 @@ SDLJoystick *joystick = NULL;
#include <bcm_host.h>
#endif
#include <atomic>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <thread>
#include "base/display.h"
#include "base/logging.h"
@@ -32,6 +34,8 @@ SDLJoystick *joystick = NULL;
#include "util/text/utf8.h"
#include "util/text/parsers.h"
#include "math/math_util.h"
#include "thin3d/GLRenderManager.h"
#include "thread/threadutil.h"
#include "Common/Vulkan/VulkanContext.h"
#include "Common/Vulkan/VulkanDebug.h"
#include "math.h"
@@ -198,6 +202,8 @@ void EGL_Close() {
}
#endif
class GLRenderManager;
class SDLGLGraphicsContext : public DummyGraphicsContext {
public:
SDLGLGraphicsContext() {
@@ -218,21 +224,30 @@ public:
}
void SwapBuffers() override {
#ifdef USING_EGL
eglSwapBuffers(g_eglDisplay, g_eglSurface);
#else
SDL_GL_SwapWindow(window_);
#endif
renderManager_->Swap();
}
Draw::DrawContext *GetDrawContext() override {
return draw_;
}
void ThreadStart() override {
renderManager_->ThreadStart();
}
bool ThreadFrame() override {
return renderManager_->ThreadFrame();
}
void ThreadEnd() override {
renderManager_->ThreadEnd();
}
private:
Draw::DrawContext *draw_ = nullptr;
SDL_Window *window_;
SDL_GLContext glContext = nullptr;
GLRenderManager *renderManager_ = nullptr;
};
// Returns 0 on success.
@@ -340,9 +355,17 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, std:
// Finally we can do the regular initialization.
CheckGLExtensions();
draw_ = Draw::T3DCreateGLContext();
renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
SetGPUBackend(GPUBackend::OPENGL);
bool success = draw_->CreatePresets();
assert(success);
renderManager_->SetSwapFunction([&]() {
#ifdef USING_EGL
eglSwapBuffers(g_eglDisplay, g_eglSurface);
#else
SDL_GL_SwapWindow(window_);
#endif
});
window_ = window;
return 0;
}
@@ -691,6 +714,40 @@ void ToggleFullScreenIfFlagSet(SDL_Window *window) {
}
}
enum class EmuThreadState {
DISABLED,
START_REQUESTED,
RUNNING,
QUIT_REQUESTED,
STOPPED,
};
static std::thread emuThread;
static std::atomic<int> emuThreadState((int)EmuThreadState::DISABLED);
static void EmuThreadFunc() {
setCurrentThreadName("Emu");
// There's no real requirement that NativeInit happen on this thread.
// We just call the update/render loop here.
emuThreadState = (int)EmuThreadState::RUNNING;
while (emuThreadState != (int)EmuThreadState::QUIT_REQUESTED) {
UpdateRunLoop();
}
emuThreadState = (int)EmuThreadState::STOPPED;
}
static void EmuThreadStart() {
emuThreadState = (int)EmuThreadState::START_REQUESTED;
emuThread = std::thread(&EmuThreadFunc);
}
static void EmuThreadStop() {
emuThreadState = (int)EmuThreadState::QUIT_REQUESTED;
emuThread.join();
emuThread = std::thread();
}
#ifdef _WIN32
#undef main
#endif
@@ -888,6 +945,11 @@ int main(int argc, char *argv[]) {
}
}
// Since we render from the main thread, there's nothing done here, but we call it to avoid confusion.
if (!graphicsContext->InitFromRenderThread(&error_message)) {
printf("Init from thread error: '%s'\n", error_message.c_str());
}
SDL_SetWindowTitle(window, (app_name_nice + " " + PPSSPP_GIT_VERSION).c_str());
#ifdef MOBILE_DEVICE
@@ -934,6 +996,11 @@ int main(int argc, char *argv[]) {
int framecount = 0;
bool mouseDown = false;
if (GetGPUBackend() == GPUBackend::OPENGL) {
EmuThreadStart();
}
graphicsContext->ThreadStart();
while (true) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
@@ -1101,7 +1168,9 @@ int main(int argc, char *argv[]) {
if (g_QuitRequested)
break;
const uint8_t *keys = SDL_GetKeyboardState(NULL);
UpdateRunLoop();
if (emuThreadState == (int)EmuThreadState::DISABLED) {
UpdateRunLoop();
}
if (g_QuitRequested)
break;
#if !defined(MOBILE_DEVICE)
@@ -1118,12 +1187,22 @@ int main(int argc, char *argv[]) {
// glsl_refresh(); // auto-reloads modified GLSL shaders once per second.
}
if (emuThreadState != (int)EmuThreadState::DISABLED) {
if (!graphicsContext->ThreadFrame())
break;
}
graphicsContext->SwapBuffers();
ToggleFullScreenIfFlagSet(window);
time_update();
framecount++;
}
graphicsContext->ThreadEnd();
graphicsContext->ShutdownFromRenderThread();
EmuThreadStop();
delete joystick;
NativeShutdownGraphics();
graphicsContext->Shutdown();
+1 -1
View File
@@ -255,7 +255,7 @@ void EmuScreen::bootComplete() {
#if !PPSSPP_PLATFORM(UWP)
if (GetGPUBackend() == GPUBackend::OPENGL) {
const char *renderer = (const char*)glGetString(GL_RENDERER);
const char *renderer = gl_extensions.model;
if (strstr(renderer, "Chainfire3D") != 0) {
osm.Show(sc->T("Chainfire3DWarning", "WARNING: Chainfire3D detected, may cause problems"), 10.0f, 0xFF30a0FF, -1, true);
} else if (strstr(renderer, "GLTools") != 0) {
+5 -10
View File
@@ -63,10 +63,6 @@
#include "Windows/W32Util/ShellUtil.h"
#endif
#if !PPSSPP_PLATFORM(UWP)
#include "gfx/gl_common.h"
#endif
extern bool VulkanMayBeAvailable();
GameSettingsScreen::GameSettingsScreen(std::string gamePath, std::string gameID, bool editThenRestore)
@@ -85,8 +81,7 @@ bool CheckSupportInstancedTessellationGLES() {
return true;
#else
// TODO: Make work with non-GL backends
int maxVertexTextureImageUnits;
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextureImageUnits);
int maxVertexTextureImageUnits = gl_extensions.maxVertexTextureUnits;
bool vertexTexture = maxVertexTextureImageUnits >= 3; // At least 3 for hardware tessellation
bool canUseInstanceID = gl_extensions.EXT_draw_instanced || gl_extensions.ARB_draw_instanced;
@@ -100,7 +95,7 @@ bool CheckSupportInstancedTessellationGLES() {
#endif
}
bool IsBackendSupportHWTess() {
bool DoesBackendSupportHWTess() {
switch (GetGPUBackend()) {
case GPUBackend::OPENGL:
return CheckSupportInstancedTessellationGLES();
@@ -353,7 +348,7 @@ void GameSettingsScreen::CreateViews() {
settingInfo_->Show(gr->T("HardwareTessellation Tip", "Uses hardware to make curves, always uses a fixed quality"), e.v);
return UI::EVENT_CONTINUE;
});
tessHWEnable_ = IsBackendSupportHWTess() && !g_Config.bSoftwareRendering && g_Config.bHardwareTransform;
tessHWEnable_ = DoesBackendSupportHWTess() && !g_Config.bSoftwareRendering && g_Config.bHardwareTransform;
tessellationHW->SetEnabledPtr(&tessHWEnable_);
// In case we're going to add few other antialiasing option like MSAA in the future.
@@ -798,13 +793,13 @@ UI::EventReturn GameSettingsScreen::OnSoftwareRendering(UI::EventParams &e) {
postProcEnable_ = !g_Config.bSoftwareRendering && (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE);
resolutionEnable_ = !g_Config.bSoftwareRendering && (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE);
bloomHackEnable_ = !g_Config.bSoftwareRendering && (g_Config.iInternalResolution != 1);
tessHWEnable_ = IsBackendSupportHWTess() && !g_Config.bSoftwareRendering && g_Config.bHardwareTransform;
tessHWEnable_ = DoesBackendSupportHWTess() && !g_Config.bSoftwareRendering && g_Config.bHardwareTransform;
return UI::EVENT_DONE;
}
UI::EventReturn GameSettingsScreen::OnHardwareTransform(UI::EventParams &e) {
vtxCacheEnable_ = !g_Config.bSoftwareRendering && g_Config.bHardwareTransform;
tessHWEnable_ = IsBackendSupportHWTess() && !g_Config.bSoftwareRendering && g_Config.bHardwareTransform;
tessHWEnable_ = DoesBackendSupportHWTess() && !g_Config.bSoftwareRendering && g_Config.bHardwareTransform;
return UI::EVENT_DONE;
}
+71 -7
View File
@@ -10,6 +10,7 @@
#include "Common/Log.h"
#include "Common/StringUtils.h"
#include "Common/GraphicsContext.h"
#include "Windows/EmuThread.h"
#include "Windows/W32Util/Misc.h"
#include "Windows/MainWindow.h"
@@ -27,8 +28,21 @@ static std::mutex emuThreadLock;
static std::thread emuThread;
static std::atomic<int> emuThreadState;
static std::mutex renderThreadLock;
static std::thread renderThread;
static std::atomic<int> renderThreadReady;
static bool useRenderThread;
static bool renderThreadFailed;
static bool renderThreadSucceeded;
static std::string g_error_message;
extern std::vector<std::wstring> GetWideCmdLine();
class GraphicsContext;
static GraphicsContext *g_graphicsContext;
enum EmuThreadStatus : int {
THREAD_NONE = 0,
THREAD_INIT,
@@ -38,10 +52,15 @@ enum EmuThreadStatus : int {
};
void EmuThreadFunc();
void RenderThreadFunc();
void EmuThread_Start() {
void EmuThread_Start(bool separateRenderThread) {
std::lock_guard<std::mutex> guard(emuThreadLock);
emuThread = std::thread(&EmuThreadFunc);
useRenderThread = separateRenderThread;
if (useRenderThread) {
renderThread = std::thread(&RenderThreadFunc);
}
}
void EmuThread_Stop() {
@@ -56,6 +75,9 @@ void EmuThread_Stop() {
Core_Stop();
Core_WaitInactive(800);
emuThread.join();
if (useRenderThread) {
renderThread.join();
}
PostMessage(MainWindow::GetHWND(), MainWindow::WM_USER_UPDATE_UI, 0, 0);
}
@@ -64,6 +86,32 @@ bool EmuThread_Ready() {
return emuThreadState == THREAD_CORE_LOOP;
}
void RenderThreadFunc() {
setCurrentThreadName("Render");
renderThreadFailed = false;
renderThreadSucceeded = false;
while (!g_graphicsContext) {
sleep_ms(10);
continue;
}
std::string error_message;
if (!g_graphicsContext->InitFromRenderThread(&error_message)) {
g_error_message = error_message;
renderThreadFailed = true;
return;
} else {
renderThreadSucceeded = true;
}
g_graphicsContext->ThreadStart();
while (g_graphicsContext->ThreadFrame()) {
continue;
}
g_graphicsContext->ThreadEnd();
g_graphicsContext->ShutdownFromRenderThread();
}
void EmuThreadFunc() {
emuThreadState = THREAD_INIT;
@@ -87,10 +135,25 @@ void EmuThreadFunc() {
host->UpdateUI();
GraphicsContext *graphicsContext = nullptr;
std::string error_string;
if (!host->InitGraphics(&error_string, &graphicsContext)) {
bool success = host->InitGraphics(&error_string, &g_graphicsContext);
if (success) {
if (!useRenderThread) {
// This is also the render thread.
success = g_graphicsContext->InitFromRenderThread(&error_string);
} else {
while (!renderThreadFailed && !renderThreadSucceeded) {
sleep_ms(10);
}
success = renderThreadSucceeded;
if (!success) {
error_string = g_error_message;
}
}
}
if (!success) {
// Before anything: are we restarting right now?
if (performingRestart) {
// Okay, switching graphics didn't work out. Probably a driver bug - fallback to restart.
@@ -139,7 +202,7 @@ void EmuThreadFunc() {
exit(1);
}
NativeInitGraphics(graphicsContext);
NativeInitGraphics(g_graphicsContext);
NativeResized();
INFO_LOG(BOOT, "Done.");
@@ -162,17 +225,18 @@ void EmuThreadFunc() {
// This way they can load a new game.
if (!Core_IsActive())
UpdateUIState(UISTATE_MENU);
Core_Run(graphicsContext);
Core_Run(g_graphicsContext);
}
shutdown:
emuThreadState = THREAD_SHUTDOWN;
NativeShutdownGraphics();
if (!useRenderThread)
g_graphicsContext->ShutdownFromRenderThread();
// NativeShutdown deletes the graphics context through host->ShutdownGraphics().
NativeShutdown();
emuThreadState = THREAD_END;
}
+1 -1
View File
@@ -17,6 +17,6 @@
#pragma once
void EmuThread_Start();
void EmuThread_Start(bool separateRenderThread);
void EmuThread_Stop();
bool EmuThread_Ready();
+55 -28
View File
@@ -21,6 +21,7 @@
#include "gfx/gl_common.h"
#include "gfx/gl_debug_log.h"
#include "gfx_es2/gpu_features.h"
#include "thin3d/GLRenderManager.h"
#include "GL/gl.h"
#include "GL/wglew.h"
#include "Core/Config.h"
@@ -34,7 +35,7 @@
#include "Windows/GPU/WindowsGLContext.h"
void WindowsGLContext::SwapBuffers() {
::SwapBuffers(hDC);
renderManager_->Swap();
// Used during fullscreen switching to prevent rendering.
if (pauseRequested) {
@@ -46,10 +47,6 @@ void WindowsGLContext::SwapBuffers() {
}
pauseRequested = false;
}
// According to some sources, doing this *after* swapbuffers can reduce frame latency
// at a large performance cost. So let's not.
// glFinish();
}
void WindowsGLContext::Pause() {
@@ -96,7 +93,7 @@ void FormatDebugOutputARB(char outStr[], size_t outStrSize, GLenum source, GLenu
case GL_DEBUG_SOURCE_APPLICATION_ARB: sourceFmt = "APPLICATION"; break;
case GL_DEBUG_SOURCE_OTHER_ARB: sourceFmt = "OTHER"; break;
}
_snprintf(sourceStr, 32, sourceFmt, source);
snprintf(sourceStr, sizeof(sourceStr), sourceFmt, source);
char typeStr[32];
const char *typeFmt = "UNDEFINED(0x%04X)";
@@ -108,30 +105,34 @@ void FormatDebugOutputARB(char outStr[], size_t outStrSize, GLenum source, GLenu
case GL_DEBUG_TYPE_PERFORMANCE_ARB: typeFmt = "PERFORMANCE"; break;
case GL_DEBUG_TYPE_OTHER_ARB: typeFmt = "OTHER"; break;
}
_snprintf(typeStr, 32, typeFmt, type);
snprintf(typeStr, sizeof(typeStr), typeFmt, type);
char severityStr[32];
const char *severityFmt = "UNDEFINED";
switch(severity)
{
case GL_DEBUG_SEVERITY_HIGH_ARB: severityFmt = "HIGH"; break;
switch (severity) {
case GL_DEBUG_SEVERITY_HIGH_ARB: severityFmt = "HIGH"; break;
case GL_DEBUG_SEVERITY_MEDIUM_ARB: severityFmt = "MEDIUM"; break;
case GL_DEBUG_SEVERITY_LOW_ARB: severityFmt = "LOW"; break;
}
_snprintf(severityStr, 32, severityFmt, severity);
_snprintf(outStr, outStrSize, "OpenGL: %s [source=%s type=%s severity=%s id=%d]", msg, sourceStr, typeStr, severityStr, id);
snprintf(severityStr, sizeof(severityStr), severityFmt, severity);
snprintf(outStr, outStrSize, "OpenGL: %s [source=%s type=%s severity=%s id=%d]\n", msg, sourceStr, typeStr, severityStr, id);
}
void DebugCallbackARB(GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const GLchar *message, GLvoid *userParam) {
(void)length;
FILE *outFile = (FILE*)userParam;
char finalMessage[256];
FormatDebugOutputARB(finalMessage, 256, source, type, id, severity, message);
FILE *outFile = (FILE *)userParam;
char finalMessage[1024];
FormatDebugOutputARB(finalMessage, sizeof(finalMessage), source, type, id, severity, message);
OutputDebugStringA(finalMessage);
// Truncate the \n before passing to our log functions.
size_t len = strlen(finalMessage);
if (len) {
finalMessage[len - 1] = '\0';
}
switch (type) {
case GL_DEBUG_TYPE_ERROR_ARB:
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
@@ -153,8 +154,15 @@ void DebugCallbackARB(GLenum source, GLenum type, GLuint id, GLenum severity,
bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_message) {
glslang::InitializeProcess();
hInst_ = hInst;
hWnd_ = window;
*error_message = "ok";
return true;
}
bool WindowsGLContext::InitFromRenderThread(std::string *error_message) {
*error_message = "ok";
hWnd = window;
GLuint PixelFormat;
// TODO: Change to use WGL_ARB_pixel_format instead
@@ -179,14 +187,14 @@ bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_mes
0, 0, 0 // Layer Masks Ignored
};
hDC = GetDC(hWnd);
hDC = GetDC(hWnd_);
if (!hDC) {
*error_message = "Failed to get a device context.";
return false; // Return FALSE
}
if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd))) {
if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd))) {
*error_message = "Can't find a suitable PixelFormat.";
return false;
}
@@ -196,7 +204,7 @@ bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_mes
return false;
}
if (!(hRC = wglCreateContext(hDC))) {
if (!(hRC = wglCreateContext(hDC))) {
*error_message = "Can't create a GL rendering context.";
return false;
}
@@ -225,7 +233,7 @@ bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_mes
HDC dc = GetDC(NULL);
u32 colour_depth = GetDeviceCaps(dc, BITSPIXEL);
ReleaseDC(NULL, dc);
if (colour_depth != 32){
if (colour_depth != 32) {
MessageBox(0, L"Please switch your display to 32-bit colour mode", L"OpenGL Error", MB_OK);
ExitProcess(1);
}
@@ -239,7 +247,7 @@ bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_mes
std::wstring title = ConvertUTF8ToWString(err->T("OpenGLDriverError", "OpenGL driver error"));
std::wstring combined = versionDetected + error;
bool yes = IDYES == MessageBox(hWnd, combined.c_str(), title.c_str(), MB_ICONERROR | MB_YESNO);
bool yes = IDYES == MessageBox(hWnd_, combined.c_str(), title.c_str(), MB_ICONERROR | MB_YESNO);
if (yes) {
// Change the config to D3D and restart.
@@ -362,8 +370,10 @@ bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_mes
CheckGLExtensions();
draw_ = Draw::T3DCreateGLContext();
renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
SetGPUBackend(GPUBackend::OPENGL);
bool success = draw_->CreatePresets(); // if we get this far, there will always be a GLSL compiler capable of compiling these.
renderManager_->SetSwapFunction([&]() {::SwapBuffers(hDC); });
assert(success);
CHECK_GL_ERROR_IF_DEBUG();
return true; // Success
@@ -376,6 +386,12 @@ void WindowsGLContext::SwapInterval(int interval) {
}
void WindowsGLContext::Shutdown() {
if (renderManager_)
renderManager_->StopThread();
glslang::FinalizeProcess();
}
void WindowsGLContext::ShutdownFromRenderThread() {
delete draw_;
draw_ = nullptr;
CloseHandle(pauseEvent);
@@ -383,26 +399,37 @@ void WindowsGLContext::Shutdown() {
if (hRC) {
// Are we able to release the DC and RC contexts?
if (!wglMakeCurrent(NULL,NULL)) {
MessageBox(NULL,L"Release of DC and RC failed.", L"SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
MessageBox(NULL, L"Release of DC and RC failed.", L"SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
}
// Are we able to delete the RC?
if (!wglDeleteContext(hRC)) {
MessageBox(NULL,L"Release rendering context failed.", L"SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
MessageBox(NULL, L"Release rendering context failed.", L"SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
}
hRC = NULL;
}
if (hDC && !ReleaseDC(hWnd,hDC)) {
if (hDC && !ReleaseDC(hWnd_, hDC)) {
DWORD err = GetLastError();
if (err != ERROR_DC_NOT_FOUND) {
MessageBox(NULL,L"Release device context failed.", L"SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
MessageBox(NULL, L"Release device context failed.", L"SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
}
hDC = NULL;
}
hWnd = NULL;
glslang::FinalizeProcess();
hWnd_ = NULL;
}
void WindowsGLContext::Resize() {
}
void WindowsGLContext::ThreadStart() {
renderManager_->ThreadStart();
}
bool WindowsGLContext::ThreadFrame() {
return renderManager_->ThreadFrame();
}
void WindowsGLContext::ThreadEnd() {
renderManager_->ThreadEnd();
}
+14 -2
View File
@@ -7,9 +7,15 @@ namespace Draw {
class DrawContext;
}
class GLRenderManager;
class WindowsGLContext : public WindowsGraphicsContext {
public:
bool Init(HINSTANCE hInst, HWND window, std::string *error_message) override;
bool InitFromRenderThread(std::string *errorMessage) override;
void ShutdownFromRenderThread() override;
void Shutdown() override;
void SwapInterval(int interval) override;
void SwapBuffers() override;
@@ -18,16 +24,22 @@ public:
// not the rendering thread or CPU thread.
void Pause() override;
void Resume() override;
void Resize() override;
void ThreadStart() override;
void ThreadEnd() override;
bool ThreadFrame() override;
Draw::DrawContext *GetDrawContext() override { return draw_; }
private:
bool renderThread_;
Draw::DrawContext *draw_;
GLRenderManager *renderManager_;
HINSTANCE hInst_;
HDC hDC; // Private GDI Device Context
HGLRC hRC; // Permanent Rendering Context
HWND hWnd; // Holds Our Window Handle
HWND hWnd_; // Holds Our Window Handle
volatile bool pauseRequested;
volatile bool resumeRequested;
HANDLE pauseEvent;
+1 -1
View File
@@ -917,7 +917,7 @@ namespace MainWindow
EmuThread_Stop();
coreState = CORE_POWERUP;
ResetUIState();
EmuThread_Start();
EmuThread_Start(false);
InputDevice::BeginPolling();
break;
+4 -2
View File
@@ -529,8 +529,10 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin
MainWindow::Minimize();
}
// Emu thread is always running!
EmuThread_Start();
// Emu thread (and render thread, if any) is always running!
// Only OpenGL uses an externally managed render thread (due to GL's single-threaded context design). Vulkan
// manages its own render thread.
EmuThread_Start(g_Config.iGPUBackend == (int)GPUBackend::OPENGL);
InputDevice::BeginPolling();
HACCEL hAccelTable = LoadAccelerators(_hInstance, (LPCTSTR)IDR_ACCELS);
+4 -1
View File
@@ -7,13 +7,16 @@
#include "GL/GLInterface/EGLAndroid.h"
#include "Core/System.h"
bool AndroidEGLGraphicsContext::Init(ANativeWindow *wnd, int backbufferWidth, int backbufferHeight, int backbufferFormat, int androidVersion) {
bool AndroidEGLGraphicsContext::InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) {
ILOG("AndroidEGLGraphicsContext::Init()");
wnd_ = wnd;
gl = HostGL_CreateGLInterface();
if (!gl) {
ELOG("ERROR: Failed to create GL interface");
return false;
}
int backbufferWidth = desiredBackbufferSizeX;
int backbufferHeight = desiredBackbufferSizeY;
ILOG("EGL interface created. Desired backbuffer size: %dx%d", backbufferWidth, backbufferHeight);
// Apparently we still have to set this through Java through setFixedSize on the bufferHolder for it to take effect...
+1 -1
View File
@@ -6,7 +6,7 @@
class AndroidEGLGraphicsContext : public AndroidGraphicsContext {
public:
AndroidEGLGraphicsContext() : draw_(nullptr), wnd_(nullptr), gl(nullptr) {}
bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override;
bool InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override;
void Shutdown() override;
void SwapBuffers() override;
void SwapInterval(int interval) override {}
+3 -1
View File
@@ -19,5 +19,7 @@ enum {
class AndroidGraphicsContext : public GraphicsContext {
public:
virtual bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) = 0;
// This is different than the base class function since on
// Android (EGL, Vulkan) we do have all this info on the render thread.
virtual bool InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) = 0;
};
+15 -6
View File
@@ -6,16 +6,25 @@
#include "Core/System.h"
AndroidJavaEGLGraphicsContext::AndroidJavaEGLGraphicsContext() {
SetGPUBackend(GPUBackend::OPENGL);
}
bool AndroidJavaEGLGraphicsContext::InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) {
ILOG("AndroidJavaEGLGraphicsContext::InitFromRenderThread");
CheckGLExtensions();
draw_ = Draw::T3DCreateGLContext();
SetGPUBackend(GPUBackend::OPENGL);
renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
bool success = draw_->CreatePresets();
assert(success);
return success;
}
void AndroidJavaEGLGraphicsContext::ShutdownFromRenderThread() {
ILOG("AndroidJavaEGLGraphicsContext::Shutdown");
renderManager_ = nullptr; // owned by draw_.
delete draw_;
draw_ = nullptr;
}
void AndroidJavaEGLGraphicsContext::Shutdown() {
ILOG("AndroidJavaEGLGraphicsContext::Shutdown");
delete draw_;
draw_ = nullptr;
NativeShutdownGraphics();
// TODO
}
+27 -2
View File
@@ -1,14 +1,25 @@
#pragma once
#include <thread>
#include <string>
#include <functional>
#include "AndroidGraphicsContext.h"
#include "thin3d/GLRenderManager.h"
// Doesn't do much. Just to fit in.
class AndroidJavaEGLGraphicsContext : public GraphicsContext {
class AndroidJavaEGLGraphicsContext : public AndroidGraphicsContext {
public:
AndroidJavaEGLGraphicsContext();
~AndroidJavaEGLGraphicsContext() {
delete draw_;
}
// This performs the actual initialization,
bool InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override;
void ShutdownFromRenderThread() override;
void Shutdown() override;
void SwapBuffers() override {}
void SwapInterval(int interval) override {}
@@ -16,7 +27,21 @@ public:
Draw::DrawContext *GetDrawContext() override {
return draw_;
}
void ThreadStart() override {
renderManager_->ThreadStart();
}
bool ThreadFrame() override {
return renderManager_->ThreadFrame();
}
void ThreadEnd() override {
renderManager_->ThreadEnd();
}
private:
Draw::DrawContext *draw_;
Draw::DrawContext *draw_ = nullptr;
GLRenderManager *renderManager_ = nullptr;
};
+3 -2
View File
@@ -60,7 +60,8 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags,
loglevel = ANDROID_LOG_WARN;
}
__android_log_print(loglevel, APP_NAME, "[%s] %s Code %d : %s", pLayerPrefix, ObjTypeToString(objType), msgCode, pMsg);
__android_log_print(loglevel, APP_NAME, "[%s] %s Code %d : %s",
pLayerPrefix, ObjTypeToString(objType), msgCode, pMsg);
// false indicates that layer should not bail-out of an
// API call that had validation failures. This may mean that the
@@ -75,7 +76,7 @@ AndroidVulkanContext::~AndroidVulkanContext() {
g_Vulkan = nullptr;
}
bool AndroidVulkanContext::Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) {
bool AndroidVulkanContext::InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) {
ILOG("AndroidVulkanContext::Init");
init_glslang();
+1 -1
View File
@@ -11,7 +11,7 @@ public:
AndroidVulkanContext() : draw_(nullptr) {}
~AndroidVulkanContext();
bool Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override;
bool InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override;
void Shutdown() override;
void SwapInterval(int interval) override;
void SwapBuffers() override;
+150 -62
View File
@@ -12,6 +12,7 @@
#include <queue>
#include <mutex>
#include <thread>
#include <atomic>
#include <jni.h>
#include <android/log.h>
@@ -50,9 +51,20 @@
#include "app-android.h"
JNIEnv *jniEnvMain;
JNIEnv *jniEnvGraphics;
JavaVM *javaVM;
bool useCPUThread = true;
enum class EmuThreadState {
DISABLED,
START_REQUESTED,
RUNNING,
QUIT_REQUESTED,
STOPPED,
};
static std::thread emuThread;
static std::atomic<int> emuThreadState((int)EmuThreadState::DISABLED);
void UpdateRunLoopAndroid(JNIEnv *env);
static AndroidAudioState *g_audioState;
@@ -97,6 +109,12 @@ static int backbuffer_format; // Android PixelFormat enum
static int desiredBackbufferSizeX;
static int desiredBackbufferSizeY;
// Cache the class loader so we can use it from native threads. Required for TextAndroid.
JavaVM* gJvm = nullptr;
static jobject gClassLoader;
static jmethodID gFindClassMethod;
static jmethodID postCommand;
static jobject nativeActivity;
static volatile bool exitRenderLoop;
@@ -114,7 +132,66 @@ static bool javaGL = true;
static std::string library_path;
static std::map<SystemPermission, PermissionStatus> permissions;
GraphicsContext *graphicsContext;
AndroidGraphicsContext *graphicsContext;
JNIEnv* getEnv() {
JNIEnv *env;
int status = gJvm->GetEnv((void**)&env, JNI_VERSION_1_6);
if(status < 0) {
status = gJvm->AttachCurrentThread(&env, NULL);
if(status < 0) {
return nullptr;
}
}
return env;
}
jclass findClass(const char* name) {
return static_cast<jclass>(getEnv()->CallObjectMethod(gClassLoader, gFindClassMethod, getEnv()->NewStringUTF(name)));
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) {
ILOG("JNI_OnLoad");
gJvm = pjvm; // cache the JavaVM pointer
auto env = getEnv();
//replace with one of your classes in the line below
auto randomClass = env->FindClass("org/ppsspp/ppsspp/NativeActivity");
jclass classClass = env->GetObjectClass(randomClass);
auto classLoaderClass = env->FindClass("java/lang/ClassLoader");
auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader",
"()Ljava/lang/ClassLoader;");
gClassLoader = env->NewGlobalRef(env->CallObjectMethod(randomClass, getClassLoaderMethod));
gFindClassMethod = env->GetMethodID(classLoaderClass, "findClass",
"(Ljava/lang/String;)Ljava/lang/Class;");
return JNI_VERSION_1_6;
}
static void EmuThreadFunc() {
JNIEnv *env;
gJvm->AttachCurrentThread(&env, nullptr);
setCurrentThreadName("Emu");
// There's no real requirement that NativeInit happen on this thread.
// We just call the update/render loop here.
emuThreadState = (int)EmuThreadState::RUNNING;
while (emuThreadState != (int)EmuThreadState::QUIT_REQUESTED) {
UpdateRunLoopAndroid(env);
}
emuThreadState = (int)EmuThreadState::STOPPED;
gJvm->DetachCurrentThread();
}
static void EmuThreadStart(JNIEnv *env) {
emuThreadState = (int)EmuThreadState::START_REQUESTED;
emuThread = std::thread(&EmuThreadFunc);
}
static void EmuThreadStop() {
emuThreadState = (int)EmuThreadState::QUIT_REQUESTED;
emuThread.join();
emuThread = std::thread();
}
static void ProcessFrameCommands(JNIEnv *env);
@@ -225,7 +302,6 @@ std::string GetJavaString(JNIEnv *env, jstring jstr) {
extern "C" void Java_org_ppsspp_ppsspp_NativeActivity_registerCallbacks(JNIEnv *env, jobject obj) {
nativeActivity = env->NewGlobalRef(obj);
postCommand = env->GetMethodID(env->GetObjectClass(obj), "postCommand", "(Ljava/lang/String;Ljava/lang/String;)V");
ILOG("Got method ID to postCommand: %p", postCommand);
}
extern "C" void Java_org_ppsspp_ppsspp_NativeActivity_unregisterCallbacks(JNIEnv *env, jobject obj) {
@@ -265,9 +341,6 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
(JNIEnv *env, jclass, jstring jmodel, jint jdeviceType, jstring jlangRegion, jstring japkpath,
jstring jdataDir, jstring jexternalDir, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam,
jint jAndroidVersion, jstring jboard) {
jniEnvMain = env;
env->GetJavaVM(&javaVM);
setCurrentThreadName("androidInit");
// Makes sure we get early permission grants.
@@ -323,8 +396,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
if (shortcut_param.empty()) {
const char *argv[2] = {app_name.c_str(), 0};
NativeInit(1, argv, user_data_path.c_str(), externalDir.c_str(), cacheDir.c_str());
}
else {
} else {
const char *argv[3] = {app_name.c_str(), shortcut_param.c_str(), 0};
NativeInit(2, argv, user_data_path.c_str(), externalDir.c_str(), cacheDir.c_str());
}
@@ -332,6 +404,33 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
// Now that we've loaded config, set javaGL.
javaGL = NativeQueryConfig("androidJavaGL") == "true";
retry:
switch (g_Config.iGPUBackend) {
case (int)GPUBackend::OPENGL:
ILOG("NativeApp.init() -- creating OpenGL context");
useCPUThread = true;
if (javaGL) {
graphicsContext = new AndroidJavaEGLGraphicsContext();
} else {
graphicsContext = new AndroidEGLGraphicsContext();
}
break;
case (int)GPUBackend::VULKAN:
ILOG("NativeApp.init() -- creating Vulkan context");
useCPUThread = false; // The Vulkan render manager manages its own thread.
graphicsContext = new AndroidVulkanContext();
break;
default:
ELOG("NativeApp.init(): iGPUBackend %d not supported. Switching to OpenGL.", (int)g_Config.iGPUBackend);
g_Config.iGPUBackend = (int)GPUBackend::OPENGL;
goto retry;
// Crash();
}
if (useCPUThread) {
ILOG("NativeApp.init() - launching emu thread");
EmuThreadStart(env);
}
ILOG("NativeApp.init() -- end");
}
@@ -381,9 +480,13 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_pause(JNIEnv *, jclass) {
}
extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) {
if (useCPUThread)
EmuThreadStop();
ILOG("NativeApp.shutdown() -- begin");
if (renderer_inited) {
ILOG("Shutting down renderer");
// This will be from the wrong thread? :/
graphicsContext->Shutdown();
delete graphicsContext;
graphicsContext = nullptr;
@@ -401,32 +504,22 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) {
// JavaEGL
extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, jobject obj) {
// Need to get the local JNI env for the graphics thread. Used later in draw_text_android.
int res = javaVM->GetEnv((void **)&jniEnvGraphics, JNI_VERSION_1_6);
if (res != JNI_OK) {
ELOG("GetEnv failed: %d", res);
}
if (javaGL && !graphicsContext) {
graphicsContext = new AndroidJavaEGLGraphicsContext();
} else if (!graphicsContext) {
_assert_msg_(G3D, false, "No graphics context in displayInit?");
}
// We should be running on the render thread here.
std::string errorMessage;
if (renderer_inited) {
ILOG("NativeApp.displayInit() restoring");
NativeShutdownGraphics();
delete graphicsContext;
graphicsContext->ThreadEnd();
graphicsContext->ShutdownFromRenderThread();
graphicsContext = new AndroidJavaEGLGraphicsContext();
NativeInitGraphics(graphicsContext);
graphicsContext->InitFromRenderThread(nullptr, 0, 0, 0, 0);
graphicsContext->ThreadStart();
ILOG("Restored.");
} else {
ILOG("NativeApp.displayInit() first time");
NativeInitGraphics(graphicsContext);
graphicsContext->InitFromRenderThread(nullptr, 0, 0, 0, 0);
graphicsContext->ThreadStart();
renderer_inited = true;
}
NativeMessageReceived("recreateviews", "");
}
@@ -470,29 +563,20 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv
}
}
// JavaEGL
extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, jobject obj) {
static bool hasSetThreadName = false;
if (!hasSetThreadName) {
hasSetThreadName = true;
setCurrentThreadName("AndroidRender");
void UpdateRunLoopAndroid(JNIEnv *env) {
// Wait for render loop to get started.
if (!renderer_inited) {
ILOG("Runloop: Waiting for displayInit");
while (!renderer_inited) {
sleep_ms(20);
}
NativeInitGraphics(graphicsContext);
}
if (renderer_inited) {
NativeUpdate();
NativeUpdate();
NativeRender(graphicsContext);
time_update();
} else {
ELOG("BAD: Ended up in nativeRender even though app has quit.%s", "");
// Shouldn't really get here. Let's draw magenta.
// TODO: Should we have GL here?
glDepthMask(GL_TRUE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glClearColor(1.0, 0.0, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
NativeRender(graphicsContext);
time_update();
std::lock_guard<std::mutex> guard(frameCommandLock);
if (!nativeActivity) {
@@ -504,6 +588,21 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env,
ProcessFrameCommands(env);
}
extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, jobject obj) {
static bool hasSetThreadName = false;
if (!hasSetThreadName) {
hasSetThreadName = true;
setCurrentThreadName("AndroidRender");
}
if (useCPUThread) {
// This is the "GPU thread".
graphicsContext->ThreadFrame();
} else {
UpdateRunLoopAndroid(env);
}
}
void System_AskForPermission(SystemPermission permission) {
switch (permission) {
case SYSTEM_PERMISSION_STORAGE:
@@ -768,18 +867,14 @@ static void ProcessFrameCommands(JNIEnv *env) {
}
extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(JNIEnv *env, jobject obj, jobject _surf) {
// Needed for Vulkan, even if we're not using the old EGL path.
exitRenderLoop = false;
// This is up here to prevent race conditions, in case we pause during init.
renderLoopRunning = true;
ANativeWindow *wnd = ANativeWindow_fromSurface(env, _surf);
// Need to get the local JNI env for the graphics thread. Used later in draw_text_android.
int res = javaVM->GetEnv((void **)&jniEnvGraphics, JNI_VERSION_1_6);
if (res != JNI_OK) {
ELOG("GetEnv failed: %d", res);
}
WLOG("runEGLRenderLoop. display_xres=%d display_yres=%d", display_xres, display_yres);
if (wnd == nullptr) {
@@ -793,14 +888,8 @@ retry:
bool vulkan = g_Config.iGPUBackend == (int)GPUBackend::VULKAN;
int tries = 0;
AndroidGraphicsContext *graphicsContext;
if (vulkan) {
graphicsContext = new AndroidVulkanContext();
} else {
graphicsContext = new AndroidEGLGraphicsContext();
}
if (!graphicsContext->Init(wnd, desiredBackbufferSizeX, desiredBackbufferSizeY, backbuffer_format, androidVersion)) {
if (!graphicsContext->InitFromRenderThread(wnd, desiredBackbufferSizeX, desiredBackbufferSizeY, backbuffer_format, androidVersion)) {
ELOG("Failed to initialize graphics context.");
if (!exitRenderLoop && (vulkan && tries < 2)) {
@@ -850,7 +939,6 @@ retry:
graphicsContext = nullptr;
renderLoopRunning = false;
WLOG("Render loop function exited.");
jniEnvGraphics = nullptr;
return true;
}
+2 -3
View File
@@ -6,8 +6,7 @@
#include <jni.h>
extern JNIEnv *jniEnvMain;
extern JNIEnv *jniEnvGraphics;
extern JavaVM *javaVM;
jclass findClass(const char* name);
JNIEnv* getEnv();
#endif
@@ -47,7 +47,7 @@ public class NativeSurfaceView extends SurfaceView implements SensorEventListene
Log.i(TAG, "MOGA initialized");
mController.setListener(this, new Handler());
} catch (Exception e) {
Log.i(TAG, "Moga failed to initialize");
// Ignore.
}
}
+3 -1
View File
@@ -75,7 +75,6 @@ LOCAL_SRC_FILES :=\
gfx_es2/draw_buffer.cpp.arm \
gfx_es2/draw_text.cpp.arm \
gfx_es2/draw_text_android.cpp.arm \
gfx/GLStateCache.cpp.arm \
gfx/gl_debug_log.cpp \
gfx/texture_atlas.cpp \
image/zim_load.cpp \
@@ -84,8 +83,11 @@ LOCAL_SRC_FILES :=\
thin3d/thin3d.cpp \
thin3d/thin3d_gl.cpp \
thin3d/thin3d_vulkan.cpp \
thin3d/GLRenderManager.cpp \
thin3d/GLQueueRunner.cpp \
thin3d/VulkanRenderManager.cpp \
thin3d/VulkanQueueRunner.cpp \
thin3d/DataFormatGL.cpp \
ui/view.cpp \
ui/viewgroup.cpp \
ui/ui.cpp \
-52
View File
@@ -1,52 +0,0 @@
#include <stdlib.h>
#include "base/logging.h"
#include "gfx/GLStateCache.h"
OpenGLState glstate;
int OpenGLState::state_count = 0;
void OpenGLState::Restore() {
int count = 0;
blend.restore(); count++;
blendEquationSeparate.restore(); count++;
blendFuncSeparate.restore(); count++;
blendColor.restore(); count++;
scissorTest.restore(); count++;
scissorRect.restore(); count++;
cullFace.restore(); count++;
cullFaceMode.restore(); count++;
frontFace.restore(); count++;
depthTest.restore(); count++;
depthRange.restore(); count++;
depthFunc.restore(); count++;
depthWrite.restore(); count++;
colorMask.restore(); count++;
viewport.restore(); count++;
stencilTest.restore(); count++;
stencilOp.restore(); count++;
stencilFunc.restore(); count++;
stencilMask.restore(); count++;
dither.restore(); count++;
#if !defined(USING_GLES2)
colorLogicOp.restore(); count++;
logicOp.restore(); count++;
#endif
arrayBuffer.restore(); count++;
elementArrayBuffer.restore(); count++;
if (count != state_count) {
FLOG("OpenGLState::Restore is missing some states");
}
}
-283
View File
@@ -1,283 +0,0 @@
#pragma once
#include <functional>
#include <cstring> // for memcmp
#include "gfx/gl_common.h"
#include "gfx_es2/gpu_features.h"
// OpenGL state cache.
// Probably only really worth it in rendering cores on weak mobile hardware.
class OpenGLState {
private:
template<GLenum cap, bool init>
class BoolState {
private:
bool v;
public:
BoolState() : v(init) {
OpenGLState::state_count++;
}
inline void set(bool value) {
if (value != v) {
v = value;
restore();
}
}
inline void force(bool value) {
bool old = v;
set(value);
v = old;
}
inline void enable() {
set(true);
}
inline void disable() {
set(false);
}
operator bool() const {
return isset();
}
inline bool isset() {
return v;
}
void restore() {
if (v)
glEnable(cap);
else
glDisable(cap);
}
};
#define STATE1(func, p1type, p1def) \
class SavedState1_##func { \
p1type p1; \
public: \
SavedState1_##func() : p1(p1def) { \
OpenGLState::state_count++; \
} \
void set(p1type newp1) { \
if (newp1 != p1) { \
p1 = newp1; \
restore(); \
} \
} \
void force(p1type newp1) { \
p1type old = p1; \
set(newp1); \
p1 = old; \
} \
inline void restore() { \
func(p1); \
} \
}
#define STATE2(func, p1type, p2type, p1def, p2def) \
class SavedState2_##func { \
p1type p1; \
p2type p2; \
public: \
SavedState2_##func() : p1(p1def), p2(p2def) { \
OpenGLState::state_count++; \
} \
inline void set(p1type newp1, p2type newp2) { \
if (newp1 != p1 || newp2 != p2) { \
p1 = newp1; \
p2 = newp2; \
restore(); \
} \
} \
void force(p1type newp1, p2type newp2) { \
p1type old1 = p1; \
p2type old2 = p2; \
set(newp1, newp2); \
p1 = old1; \
p2 = old2; \
} \
inline void restore() { \
func(p1, p2); \
} \
}
#define STATE3(func, p1type, p2type, p3type, p1def, p2def, p3def) \
class SavedState3_##func { \
p1type p1; \
p2type p2; \
p3type p3; \
public: \
SavedState3_##func() : p1(p1def), p2(p2def), p3(p3def) { \
OpenGLState::state_count++; \
} \
inline void set(p1type newp1, p2type newp2, p3type newp3) { \
if (newp1 != p1 || newp2 != p2 || newp3 != p3) { \
p1 = newp1; \
p2 = newp2; \
p3 = newp3; \
restore(); \
} \
} \
void force(p1type newp1, p2type newp2, p3type newp3) { \
p1type old1 = p1; \
p2type old2 = p2; \
p3type old3 = p3; \
set(newp1, newp2, newp3); \
p1 = old1; \
p2 = old2; \
p3 = old3; \
} \
inline void restore() { \
func(p1, p2, p3); \
} \
}
#define STATE4(func, p1type, p2type, p3type, p4type, p1def, p2def, p3def, p4def) \
class SavedState4_##func { \
p1type p1; \
p2type p2; \
p3type p3; \
p4type p4; \
public: \
SavedState4_##func() : p1(p1def), p2(p2def), p3(p3def), p4(p4def) { \
OpenGLState::state_count++; \
} \
inline void set(p1type newp1, p2type newp2, p3type newp3, p4type newp4) { \
if (newp1 != p1 || newp2 != p2 || newp3 != p3 || newp4 != p4) { \
p1 = newp1; \
p2 = newp2; \
p3 = newp3; \
p4 = newp4; \
restore(); \
} \
} \
void force(p1type newp1, p2type newp2, p3type newp3, p4type newp4) { \
p1type old1 = p1; \
p2type old2 = p2; \
p3type old3 = p3; \
p4type old4 = p4; \
set(newp1, newp2, newp3, newp4); \
p1 = old1; \
p2 = old2; \
p3 = old3; \
p4 = old4; \
} \
inline void restore() { \
func(p1, p2, p3, p4); \
} \
}
#define STATEFLOAT4(func, def) \
class SavedState4_##func { \
float p[4]; \
public: \
SavedState4_##func() { \
for (int i = 0; i < 4; i++) {p[i] = def;} \
OpenGLState::state_count++; \
} \
inline void set(const float v[4]) { \
if (memcmp(p, v, sizeof(float) * 4)) { \
memcpy(p, v, sizeof(float) * 4); \
restore(); \
} \
} \
void force(const float v[4]) { \
float old[4]; \
memcpy(old, p, sizeof(float) * 4); \
set(v); \
memcpy(p, old, sizeof(float) * 4); \
} \
inline void restore() { \
func(p[0], p[1], p[2], p[3]); \
} \
}
#define STATEBIND(func, target) \
class SavedBind_##func_##target { \
GLuint val_; \
public: \
SavedBind_##func_##target() { \
val_ = 0; \
OpenGLState::state_count++; \
} \
inline void bind(GLuint val) { \
if (val_ != val) { \
val_ = val; \
restore(); \
} \
} \
inline void force(GLuint val) { \
GLuint old = val_; \
bind(val); \
val_ = old; \
} \
inline void unbind() { \
bind(0); \
} \
inline void restore() { \
func(target, val_); \
} \
}
public:
static int state_count;
OpenGLState() {}
void Restore();
// When adding a state here, don't forget to add it to OpenGLState::Restore() too
// Blending
BoolState<GL_BLEND, false> blend;
STATE4(glBlendFuncSeparate, GLenum, GLenum, GLenum, GLenum, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) blendFuncSeparate;
// On OpenGL ES, using minmax blend requires glBlendEquationEXT (in theory at least but I don't think it's true in practice)
STATE2(glBlendEquationSeparate, GLenum, GLenum, GL_FUNC_ADD, GL_FUNC_ADD) blendEquationSeparate;
STATEFLOAT4(glBlendColor, 1.0f) blendColor;
// Logic Ops. Not available on OpenGL ES at all.
#if !defined(USING_GLES2)
BoolState<GL_COLOR_LOGIC_OP, false> colorLogicOp;
STATE1(glLogicOp, GLenum, GL_COPY) logicOp;
#endif
// Dither
BoolState<GL_DITHER, false> dither;
// Cull Face
BoolState<GL_CULL_FACE, false> cullFace;
STATE1(glCullFace, GLenum, GL_FRONT) cullFaceMode;
STATE1(glFrontFace, GLenum, GL_CCW) frontFace;
// Depth Test
BoolState<GL_DEPTH_TEST, false> depthTest;
#if defined(USING_GLES2)
STATE2(glDepthRangef, float, float, 0.f, 1.f) depthRange;
#else
STATE2(glDepthRange, double, double, 0.0, 1.0) depthRange;
#endif
STATE1(glDepthFunc, GLenum, GL_LESS) depthFunc;
STATE1(glDepthMask, GLboolean, GL_TRUE) depthWrite;
// Color Mask
STATE4(glColorMask, bool, bool, bool, bool, true, true, true, true) colorMask;
// Viewport
STATE4(glViewport, GLint, GLint, GLsizei, GLsizei, 0, 0, 128, 128) viewport;
// Scissor Test
BoolState<GL_SCISSOR_TEST, false> scissorTest;
STATE4(glScissor, GLint, GLint, GLsizei, GLsizei, 0, 0, 128, 128) scissorRect;
// Stencil Test
BoolState<GL_STENCIL_TEST, false> stencilTest;
STATE3(glStencilOp, GLenum, GLenum, GLenum, GL_KEEP, GL_KEEP, GL_KEEP) stencilOp;
STATE3(glStencilFunc, GLenum, GLint, GLuint, GL_ALWAYS, 0, 0xFF) stencilFunc;
STATE1(glStencilMask, GLuint, 0xFF) stencilMask;
STATEBIND(glBindBuffer, GL_ARRAY_BUFFER) arrayBuffer;
STATEBIND(glBindBuffer, GL_ELEMENT_ARRAY_BUFFER) elementArrayBuffer;
};
#undef STATE1
#undef STATE2
extern OpenGLState glstate;
+2 -2
View File
@@ -16,9 +16,9 @@
#include <jni.h>
TextDrawerAndroid::TextDrawerAndroid(Draw::DrawContext *draw) : TextDrawer(draw) {
env_ = jniEnvGraphics;
env_ = getEnv();
const char *textRendererClassName = "org/ppsspp/ppsspp/TextRenderer";
jclass localClass = env_->FindClass(textRendererClassName);
jclass localClass = findClass(textRendererClassName);
cls_textRenderer = reinterpret_cast<jclass>(env_->NewGlobalRef(localClass));
ILOG("cls_textRender: %p", cls_textRenderer);
if (cls_textRenderer) {
+2
View File
@@ -393,6 +393,8 @@ void CheckGLExtensions() {
}
#endif
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &gl_extensions.maxVertexTextureUnits);
// This is probably a waste of time, implementations lie.
if (gl_extensions.IsGLES || strstr(extString, "GL_ARB_ES2_compatibility") || gl_extensions.VersionGEThan(4, 1)) {
const GLint precisions[6] = {
+2
View File
@@ -96,6 +96,8 @@ struct GLExtensions {
int range[2][6][2]; // [vs,fs][lowf,mediumf,highf,lowi,mediumi,highi][min,max]
int precision[2][6]; // [vs,fs][lowf...]
int maxVertexTextureUnits;
// greater-or-equal than
bool VersionGEThan(int major, int minor, int sub = 0);
};
+6 -2
View File
@@ -232,7 +232,6 @@
<ClInclude Include="file\free.h" />
<ClInclude Include="gfx\d3d9_shader.h" />
<ClInclude Include="gfx\d3d9_state.h" />
<ClInclude Include="gfx\GLStateCache.h" />
<ClInclude Include="gfx\gl_common.h" />
<ClInclude Include="gfx\gl_debug_log.h" />
<ClInclude Include="gfx\texture_atlas.h" />
@@ -241,6 +240,9 @@
<ClInclude Include="gfx_es2\draw_text_win.h" />
<ClInclude Include="thin3d\d3d11_loader.h" />
<ClInclude Include="thin3d\DataFormat.h" />
<ClInclude Include="thin3d\DataFormatGL.h" />
<ClInclude Include="thin3d\GLQueueRunner.h" />
<ClInclude Include="thin3d\GLRenderManager.h" />
<ClInclude Include="thin3d\VulkanQueueRunner.h" />
<ClInclude Include="thin3d\VulkanRenderManager.h" />
<ClInclude Include="util\text\wrap_text.h" />
@@ -691,7 +693,6 @@
<ClCompile Include="file\free.cpp" />
<ClCompile Include="gfx\d3d9_shader.cpp" />
<ClCompile Include="gfx\d3d9_state.cpp" />
<ClCompile Include="gfx\GLStateCache.cpp" />
<ClCompile Include="gfx\gl_debug_log.cpp" />
<ClCompile Include="gfx\texture_atlas.cpp" />
<ClCompile Include="gfx_es2\draw_text_android.cpp" />
@@ -699,6 +700,9 @@
<ClCompile Include="gfx_es2\draw_text_win.cpp" />
<ClCompile Include="math\dataconv.cpp" />
<ClCompile Include="thin3d\d3d11_loader.cpp" />
<ClCompile Include="thin3d\DataFormatGL.cpp" />
<ClCompile Include="thin3d\GLQueueRunner.cpp" />
<ClCompile Include="thin3d\GLRenderManager.cpp" />
<ClCompile Include="thin3d\thin3d_d3d11.cpp" />
<ClCompile Include="thin3d\VulkanQueueRunner.cpp" />
<ClCompile Include="thin3d\VulkanRenderManager.cpp" />
+19 -7
View File
@@ -302,9 +302,6 @@
<ClInclude Include="gfx\d3d9_state.h">
<Filter>gfx</Filter>
</ClInclude>
<ClInclude Include="gfx\GLStateCache.h">
<Filter>gfx</Filter>
</ClInclude>
<ClInclude Include="thin3d\d3d11_loader.h">
<Filter>thin3d</Filter>
</ClInclude>
@@ -332,6 +329,15 @@
<ClInclude Include="..\..\Qt\QtMain.h">
<Filter>base</Filter>
</ClInclude>
<ClInclude Include="thin3d\GLRenderManager.h">
<Filter>thin3d</Filter>
</ClInclude>
<ClInclude Include="thin3d\GLQueueRunner.h">
<Filter>thin3d</Filter>
</ClInclude>
<ClInclude Include="thin3d\DataFormatGL.h">
<Filter>thin3d</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="gfx\gl_debug_log.cpp">
@@ -769,9 +775,6 @@
<ClCompile Include="gfx\d3d9_state.cpp">
<Filter>gfx</Filter>
</ClCompile>
<ClCompile Include="gfx\GLStateCache.cpp">
<Filter>gfx</Filter>
</ClCompile>
<ClCompile Include="thin3d\d3d11_loader.cpp">
<Filter>thin3d</Filter>
</ClCompile>
@@ -799,6 +802,15 @@
<ClCompile Include="ui\ui_tween.cpp">
<Filter>ui</Filter>
</ClCompile>
<ClCompile Include="thin3d\GLQueueRunner.cpp">
<Filter>thin3d</Filter>
</ClCompile>
<ClCompile Include="thin3d\GLRenderManager.cpp">
<Filter>thin3d</Filter>
</ClCompile>
<ClCompile Include="thin3d\DataFormatGL.cpp">
<Filter>thin3d</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="gfx">
@@ -871,4 +883,4 @@
<UniqueIdentifier>{06c6305a-a646-485b-85b9-645a24dd6553}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
</Project>
+89
View File
@@ -0,0 +1,89 @@
#include "thin3d/DataFormatGL.h"
#include "base/logging.h"
#include "Common/Log.h"
namespace Draw {
// TODO: Also output storage format (GL_RGBA8 etc) for modern GL usage.
bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment) {
alignment = 4;
switch (fmt) {
case DataFormat::R8G8B8A8_UNORM:
internalFormat = GL_RGBA;
format = GL_RGBA;
type = GL_UNSIGNED_BYTE;
break;
case DataFormat::D32F:
internalFormat = GL_DEPTH_COMPONENT;
format = GL_DEPTH_COMPONENT;
type = GL_FLOAT;
break;
#ifndef USING_GLES2
case DataFormat::S8:
internalFormat = GL_STENCIL_INDEX;
format = GL_STENCIL_INDEX;
type = GL_UNSIGNED_BYTE;
alignment = 1;
break;
#endif
case DataFormat::R8G8B8_UNORM:
internalFormat = GL_RGB;
format = GL_RGB;
type = GL_UNSIGNED_BYTE;
alignment = 1;
break;
case DataFormat::B4G4R4A4_UNORM_PACK16:
internalFormat = GL_RGBA;
format = GL_RGBA;
type = GL_UNSIGNED_SHORT_4_4_4_4;
alignment = 2;
break;
case DataFormat::B5G6R5_UNORM_PACK16:
internalFormat = GL_RGB;
format = GL_RGB;
type = GL_UNSIGNED_SHORT_5_6_5;
alignment = 2;
break;
case DataFormat::B5G5R5A1_UNORM_PACK16:
internalFormat = GL_RGBA;
format = GL_RGBA;
type = GL_UNSIGNED_SHORT_5_5_5_1;
alignment = 2;
break;
#ifndef USING_GLES2
case DataFormat::A4R4G4B4_UNORM_PACK16:
internalFormat = GL_RGBA;
format = GL_RGBA;
type = GL_UNSIGNED_SHORT_4_4_4_4_REV;
alignment = 2;
break;
case DataFormat::R5G6B5_UNORM_PACK16:
internalFormat = GL_RGB;
format = GL_RGB;
type = GL_UNSIGNED_SHORT_5_6_5_REV;
alignment = 2;
break;
case DataFormat::A1R5G5B5_UNORM_PACK16:
internalFormat = GL_RGBA;
format = GL_RGBA;
type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
alignment = 2;
break;
#endif
default:
return false;
}
return true;
}
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "gfx/gl_common.h"
#include "thin3d/DataFormat.h"
namespace Draw {
bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment);
}
File diff suppressed because it is too large. Load diff
+392
View File
@@ -0,0 +1,392 @@
#pragma once
#include <cstdint>
#include <vector>
#include <unordered_map>
#include "gfx/gl_common.h"
#include "thin3d/DataFormat.h"
struct GLRViewport {
float x, y, w, h, minZ, maxZ;
};
struct GLRect2D {
int x, y, w, h;
};
struct GLOffset2D {
int x, y;
};
class GLRShader;
class GLRTexture;
class GLRBuffer;
class GLRFramebuffer;
class GLRProgram;
class GLRInputLayout;
enum class GLRRenderCommand : uint8_t {
DEPTH,
STENCILFUNC,
STENCILOP,
BLEND,
BLENDCOLOR,
LOGICOP,
UNIFORM4I,
UNIFORM4F,
UNIFORMMATRIX,
TEXTURESAMPLER,
TEXTURELOD,
VIEWPORT,
SCISSOR,
RASTER,
CLEAR,
INVALIDATE,
BINDPROGRAM,
BINDTEXTURE,
BIND_FB_TEXTURE,
BIND_VERTEX_BUFFER,
BIND_BUFFER,
GENMIPS,
DRAW,
DRAW_INDEXED,
PUSH_CONSTANTS,
};
// TODO: Bloated since the biggest struct decides the size. Will need something more efficient (separate structs with shared
// type field, smashed right after each other?)
// Also, all GLenums are really only 16 bits.
struct GLRRenderData {
GLRRenderCommand cmd;
union {
struct {
GLboolean enabled;
GLenum srcColor;
GLenum dstColor;
GLenum srcAlpha;
GLenum dstAlpha;
GLenum funcColor;
GLenum funcAlpha;
int mask;
} blend;
struct {
float color[4];
} blendColor;
struct {
GLboolean enabled;
GLenum logicOp;
} logic;
struct {
GLboolean enabled;
GLboolean write;
GLenum func;
} depth;
struct {
GLboolean enabled;
GLenum func;
uint8_t ref;
uint8_t compareMask;
} stencilFunc;
struct {
GLenum sFail;
GLenum zFail;
GLenum pass;
uint8_t writeMask;
} stencilOp; // also write mask
struct {
GLenum mode; // primitive
GLint buffer;
GLint first;
GLint count;
} draw;
struct {
GLenum mode; // primitive
GLint count;
GLint instances;
GLint indexType;
void *indices;
} drawIndexed;
struct {
const char *name; // if null, use loc
GLint *loc; // NOTE: This is a pointer so we can immediately use things that are "queried" during program creation.
GLint count;
float v[4];
} uniform4;
struct {
const char *name; // if null, use loc
GLint *loc;
float m[16];
} uniformMatrix4;
struct {
uint32_t clearColor;
float clearZ;
int clearStencil;
int clearMask; // VK_IMAGE_ASPECT_COLOR_BIT etc
int colorMask; // Like blend, but for the clear.
} clear;
struct {
int slot;
GLRTexture *texture;
} texture;
struct {
int slot;
GLRFramebuffer *framebuffer;
int aspect;
} bind_fb_texture;
struct {
GLRBuffer *buffer;
GLuint target;
} bind_buffer;
struct {
GLRProgram *program;
} program;
struct {
GLRInputLayout *inputLayout;
GLRBuffer *buffer;
size_t offset;
} bindVertexBuffer;
struct {
GLenum wrapS;
GLenum wrapT;
GLenum magFilter;
GLenum minFilter; // also includes mip. GL...
float anisotropy;
} textureSampler;
struct {
float minLod;
float maxLod;
float lodBias;
} textureLod;
struct {
GLRViewport vp;
} viewport;
struct {
GLRect2D rc;
} scissor;
struct {
GLboolean cullEnable;
GLenum frontFace;
GLenum cullFace;
GLboolean ditherEnable;
} raster;
};
};
// Unlike in Vulkan, we can't create stuff on the main thread, but need to
// defer this too. A big benefit will be that we'll be able to do all creation
// at the start of the frame.
enum class GLRInitStepType : uint8_t {
CREATE_TEXTURE,
CREATE_SHADER,
CREATE_PROGRAM,
CREATE_BUFFER,
CREATE_INPUT_LAYOUT,
CREATE_FRAMEBUFFER,
TEXTURE_IMAGE,
TEXTURE_FINALIZE,
BUFFER_SUBDATA,
};
struct GLRInitStep {
GLRInitStep(GLRInitStepType _type) : stepType(_type) {}
GLRInitStepType stepType;
union {
struct {
GLRTexture *texture;
GLenum target;
} create_texture;
struct {
GLRShader *shader;
// This char arrays needs to be allocated with new[].
char *code;
GLuint stage;
} create_shader;
struct {
GLRProgram *program;
GLRShader *shaders[3];
int num_shaders;
bool support_dual_source;
} create_program;
struct {
GLRBuffer *buffer;
int size;
GLuint usage;
} create_buffer;
struct {
GLRInputLayout *inputLayout;
} create_input_layout;
struct {
GLRFramebuffer *framebuffer;
} create_framebuffer;
struct {
GLRBuffer *buffer;
int offset;
int size;
uint8_t *data; // owned, delete[]-d
bool deleteData;
} buffer_subdata;
struct {
GLRTexture *texture;
GLenum internalFormat;
GLenum format;
GLenum type;
int level;
int width;
int height;
bool linearFilter;
uint8_t *data; // owned, delete[]-d
} texture_image;
struct {
GLRTexture *texture;
int maxLevel;
bool genMips;
} texture_finalize;
};
};
enum class GLRStepType : uint8_t {
RENDER,
COPY,
BLIT,
READBACK,
READBACK_IMAGE,
};
enum class GLRRenderPassAction {
DONT_CARE,
CLEAR,
KEEP,
};
class GLRFramebuffer;
enum {
GLR_ASPECT_COLOR = 1,
GLR_ASPECT_DEPTH = 2,
GLR_ASPECT_STENCIL = 3,
};
struct GLRStep {
GLRStep(GLRStepType _type) : stepType(_type) {}
GLRStepType stepType;
std::vector<GLRRenderData> commands;
union {
struct {
GLRFramebuffer *framebuffer;
int numDraws;
} render;
struct {
GLRFramebuffer *src;
GLRFramebuffer *dst;
GLRect2D srcRect;
GLOffset2D dstPos;
int aspectMask;
} copy;
struct {
GLRFramebuffer *src;
GLRFramebuffer *dst;
GLRect2D srcRect;
GLRect2D dstRect;
int aspectMask;
GLboolean filter;
} blit;
struct {
int aspectMask;
GLRFramebuffer *src;
GLRect2D srcRect;
Draw::DataFormat dstFormat;
} readback;
struct {
GLRTexture *texture;
GLRect2D srcRect;
int mipLevel;
} readback_image;
};
};
class GLQueueRunner {
public:
GLQueueRunner() {}
void RunInitSteps(const std::vector<GLRInitStep> &steps);
void RunSteps(const std::vector<GLRStep *> &steps);
void LogSteps(const std::vector<GLRStep *> &steps);
void CreateDeviceObjects();
void DestroyDeviceObjects();
inline int RPIndex(GLRRenderPassAction color, GLRRenderPassAction depth) {
return (int)depth * 3 + (int)color;
}
void CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels);
void Resize(int width, int height) {
targetWidth_ = width;
targetHeight_ = height;
}
bool SawOutOfMemory() {
return sawOutOfMemory_;
}
std::string GetGLString(int name) const {
auto it = glStrings_.find(name);
return it != glStrings_.end() ? it->second : "";
}
private:
void InitCreateFramebuffer(const GLRInitStep &step);
void PerformBindFramebufferAsRenderTarget(const GLRStep &pass);
void PerformRenderPass(const GLRStep &pass);
void PerformCopy(const GLRStep &pass);
void PerformBlit(const GLRStep &pass);
void PerformReadback(const GLRStep &pass);
void PerformReadbackImage(const GLRStep &pass);
void LogRenderPass(const GLRStep &pass);
void LogCopy(const GLRStep &pass);
void LogBlit(const GLRStep &pass);
void LogReadback(const GLRStep &pass);
void LogReadbackImage(const GLRStep &pass);
void fbo_ext_create(const GLRInitStep &step);
void fbo_bind_fb_target(bool read, GLuint name);
GLenum fbo_get_fb_target(bool read, GLuint **cached);
void fbo_unbind();
GLRFramebuffer *curFB_ = nullptr;
GLuint globalVAO_ = 0;
int curFBWidth_ = 0;
int curFBHeight_ = 0;
int targetWidth_ = 0;
int targetHeight_ = 0;
// Readback buffer. Currently we only support synchronous readback, so we only really need one.
// We size it generously.
uint8_t *readbackBuffer_ = nullptr;
int readbackBufferSize_ = 0;
// Temp buffer for color conversion
uint8_t *tempBuffer_ = nullptr;
int tempBufferSize_ = 0;
float maxAnisotropyLevel_ = 0.0f;
// Framebuffer state?
GLuint currentDrawHandle_ = 0;
GLuint currentReadHandle_ = 0;
GLuint AllocTextureName();
// Texture name cache. Ripped straight from TextureCacheGLES.
std::vector<GLuint> nameCache_;
std::unordered_map<int, std::string> glStrings_;
bool sawOutOfMemory_ = false;
};
+622
View File
@@ -0,0 +1,622 @@
#include <cassert>
#include "GLRenderManager.h"
#include "gfx_es2/gpu_features.h"
#include "thread/threadutil.h"
#include "base/logging.h"
#include "GPU/GPUState.h"
#if 0 // def _DEBUG
#define VLOG ILOG
#else
#define VLOG(...)
#endif
void GLDeleter::Perform() {
for (auto shader : shaders) {
delete shader;
}
shaders.clear();
for (auto program : programs) {
delete program;
}
programs.clear();
for (auto buffer : buffers) {
delete buffer;
}
buffers.clear();
for (auto texture : textures) {
delete texture;
}
textures.clear();
for (auto inputLayout : inputLayouts) {
delete inputLayout;
}
inputLayouts.clear();
for (auto framebuffer : framebuffers) {
delete framebuffer;
}
framebuffers.clear();
}
GLRenderManager::GLRenderManager() {
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
}
if (!useThread_) {
// The main thread is also the render thread.
ThreadStart();
}
}
GLRenderManager::~GLRenderManager() {
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
}
if (!useThread_) {
// The main thread is also the render thread.
ThreadEnd();
}
}
void GLRenderManager::ThreadStart() {
queueRunner_.CreateDeviceObjects();
threadFrame_ = threadInitFrame_;
}
void GLRenderManager::ThreadEnd() {
// Wait for any shutdown to complete in StopThread().
std::unique_lock<std::mutex> lock(mutex_);
queueRunner_.DestroyDeviceObjects();
VLOG("PULL: Quitting");
}
bool GLRenderManager::ThreadFrame() {
std::unique_lock<std::mutex> lock(mutex_);
if (!run_)
return false;
// In case of syncs or other partial completion, we keep going until we complete a frame.
do {
if (nextFrame) {
threadFrame_++;
if (threadFrame_ >= MAX_INFLIGHT_FRAMES)
threadFrame_ = 0;
}
FrameData &frameData = frameData_[threadFrame_];
{
std::unique_lock<std::mutex> lock(frameData.pull_mutex);
while (!frameData.readyForRun && run_) {
VLOG("PULL: Waiting for frame[%d].readyForRun", threadFrame_);
frameData.pull_condVar.wait(lock);
}
if (!frameData.readyForRun && !run_) {
// This means we're out of frames to render and run_ is false, so bail.
return false;
}
VLOG("PULL: frame[%d].readyForRun = false", threadFrame_);
frameData.readyForRun = false;
// Previously we had a quick exit here that avoided calling Run() if run_ was suddenly false,
// but that created a race condition where frames could end up not finished properly on resize etc.
// Only increment next time if we're done.
nextFrame = frameData.type == GLRRunType::END;
assert(frameData.type == GLRRunType::END || frameData.type == GLRRunType::SYNC);
}
VLOG("PULL: Running frame %d", threadFrame_);
if (firstFrame) {
ILOG("Running first frame (%d)", threadFrame_);
firstFrame = false;
}
Run(threadFrame_);
VLOG("PULL: Finished frame %d", threadFrame_);
} while (!nextFrame);
return true;
}
void GLRenderManager::StopThread() {
// Since we don't control the thread directly, this will only pause the thread.
if (useThread_ && run_) {
run_ = false;
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
auto &frameData = frameData_[i];
{
std::unique_lock<std::mutex> lock(frameData.push_mutex);
frameData.push_condVar.notify_all();
}
{
std::unique_lock<std::mutex> lock(frameData.pull_mutex);
frameData.pull_condVar.notify_all();
}
}
// Wait until we've definitely stopped the threadframe.
std::unique_lock<std::mutex> lock(mutex_);
ILOG("GL submission thread paused. Frame=%d", curFrame_);
// Eat whatever has been queued up for this frame if anything.
Wipe();
// Wait for any fences to finish and be resignaled, so we don't have sync issues.
// Also clean out any queued data, which might refer to things that might not be valid
// when we restart...
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
auto &frameData = frameData_[i];
if (frameData.readyForRun || frameData.steps.size() != 0) {
Crash();
}
frameData.readyForRun = false;
frameData.readyForSubmit = false;
for (size_t i = 0; i < frameData.steps.size(); i++) {
delete frameData.steps[i];
}
frameData.steps.clear();
frameData.initSteps.clear();
std::unique_lock<std::mutex> lock(frameData.push_mutex);
while (!frameData.readyForFence) {
VLOG("PUSH: Waiting for frame[%d].readyForFence = 1 (stop)", i);
frameData.push_condVar.wait(lock);
}
}
} else {
ILOG("GL submission thread was already paused.");
}
}
void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, GLRRenderPassAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil) {
assert(insideFrame_);
// Eliminate dupes.
if (steps_.size() && steps_.back()->render.framebuffer == fb && steps_.back()->stepType == GLRStepType::RENDER) {
if (color != GLRRenderPassAction::CLEAR && depth != GLRRenderPassAction::CLEAR && stencil != GLRRenderPassAction::CLEAR) {
// We don't move to a new step, this bind was unnecessary and we can safely skip it.
return;
}
}
if (curRenderStep_ && curRenderStep_->commands.size() == 0) {
VLOG("Empty render step. Usually happens after uploading pixels..");
}
GLRStep *step = new GLRStep{ GLRStepType::RENDER };
// This is what queues up new passes, and can end previous ones.
step->render.framebuffer = fb;
step->render.numDraws = 0;
steps_.push_back(step);
GLuint clearMask = 0;
GLRRenderData data;
data.cmd = GLRRenderCommand::CLEAR;
if (color == GLRRenderPassAction::CLEAR) {
clearMask |= GL_COLOR_BUFFER_BIT;
data.clear.clearColor = clearColor;
}
if (depth == GLRRenderPassAction::CLEAR) {
clearMask |= GL_DEPTH_BUFFER_BIT;
data.clear.clearZ = clearDepth;
}
if (stencil == GLRRenderPassAction::CLEAR) {
clearMask |= GL_STENCIL_BUFFER_BIT;
data.clear.clearStencil = clearStencil;
}
if (clearMask) {
data.clear.clearMask = clearMask;
data.clear.colorMask = 0xF;
step->commands.push_back(data);
}
curRenderStep_ = step;
// Every step clears this state.
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE);
}
void GLRenderManager::BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit, int attachment) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::BIND_FB_TEXTURE };
data.bind_fb_texture.slot = binding;
data.bind_fb_texture.framebuffer = fb;
data.bind_fb_texture.aspect = aspectBit;
curRenderStep_->commands.push_back(data);
}
void GLRenderManager::CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLOffset2D dstPos, int aspectMask) {
GLRStep *step = new GLRStep{ GLRStepType::COPY };
step->copy.srcRect = srcRect;
step->copy.dstPos = dstPos;
step->copy.src = src;
step->copy.dst = dst;
step->copy.aspectMask = aspectMask;
steps_.push_back(step);
// Every step clears this state.
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE);
}
void GLRenderManager::BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLRect2D dstRect, int aspectMask, bool filter) {
GLRStep *step = new GLRStep{ GLRStepType::BLIT };
step->blit.srcRect = srcRect;
step->blit.dstRect = dstRect;
step->blit.src = src;
step->blit.dst = dst;
step->blit.aspectMask = aspectMask;
step->blit.filter = filter;
steps_.push_back(step);
// Every step clears this state.
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE);
}
bool GLRenderManager::CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) {
GLRStep *step = new GLRStep{ GLRStepType::READBACK };
step->readback.src = src;
step->readback.srcRect = { x, y, w, h };
step->readback.aspectMask = aspectBits;
step->readback.dstFormat = destFormat;
steps_.push_back(step);
// Every step clears this state.
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE);
curRenderStep_ = nullptr;
FlushSync();
Draw::DataFormat srcFormat;
if (aspectBits & GL_COLOR_BUFFER_BIT) {
srcFormat = Draw::DataFormat::R8G8B8A8_UNORM;
} else if (aspectBits & GL_STENCIL_BUFFER_BIT) {
// Copies from stencil are always S8.
srcFormat = Draw::DataFormat::S8;
} else if (aspectBits & GL_DEPTH_BUFFER_BIT) {
// TODO: Do this properly.
srcFormat = Draw::DataFormat::D24_S8;
} else {
_assert_(false);
}
queueRunner_.CopyReadbackBuffer(w, h, srcFormat, destFormat, pixelStride, pixels);
return true;
}
void GLRenderManager::CopyImageToMemorySync(GLRTexture *texture, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) {
GLRStep *step = new GLRStep{ GLRStepType::READBACK_IMAGE };
step->readback_image.texture = texture;
step->readback_image.mipLevel = mipLevel;
step->readback_image.srcRect = { x, y, w, h };
steps_.push_back(step);
// Every step clears this state.
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE);
curRenderStep_ = nullptr;
FlushSync();
queueRunner_.CopyReadbackBuffer(w, h, Draw::DataFormat::R8G8B8A8_UNORM, destFormat, pixelStride, pixels);
}
void GLRenderManager::BeginFrame() {
VLOG("BeginFrame");
int curFrame = GetCurFrame();
FrameData &frameData = frameData_[curFrame];
// Make sure the very last command buffer from the frame before the previous has been fully executed.
if (useThread_) {
std::unique_lock<std::mutex> lock(frameData.push_mutex);
while (!frameData.readyForFence) {
VLOG("PUSH: Waiting for frame[%d].readyForFence = 1", curFrame);
frameData.push_condVar.wait(lock);
}
frameData.readyForFence = false;
frameData.readyForSubmit = true;
}
VLOG("PUSH: Fencing %d", curFrame);
// vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX);
// vkResetFences(device, 1, &frameData.fence);
// glFenceSync(...)
// Must be after the fence - this performs deletes.
VLOG("PUSH: BeginFrame %d", curFrame);
if (!run_) {
WLOG("BeginFrame while !run_!");
}
// vulkan_->BeginFrame();
// In GL, we have to do deletes on the submission thread.
insideFrame_ = true;
}
void GLRenderManager::Finish() {
curRenderStep_ = nullptr;
int curFrame = GetCurFrame();
FrameData &frameData = frameData_[curFrame];
if (!useThread_) {
frameData.steps = std::move(steps_);
steps_.clear();
frameData.initSteps = std::move(initSteps_);
initSteps_.clear();
frameData.type = GLRRunType::END;
Run(curFrame);
} else {
std::unique_lock<std::mutex> lock(frameData.pull_mutex);
VLOG("PUSH: Frame[%d].readyForRun = true", curFrame);
frameData.steps = std::move(steps_);
steps_.clear();
frameData.initSteps = std::move(initSteps_);
initSteps_.clear();
frameData.readyForRun = true;
frameData.type = GLRRunType::END;
frameData.pull_condVar.notify_all();
}
curFrame_++;
if (curFrame_ >= MAX_INFLIGHT_FRAMES)
curFrame_ = 0;
insideFrame_ = false;
}
void GLRenderManager::BeginSubmitFrame(int frame) {
FrameData &frameData = frameData_[frame];
if (!frameData.hasBegun) {
frameData.hasBegun = true;
}
}
void GLRenderManager::Submit(int frame, bool triggerFence) {
FrameData &frameData = frameData_[frame];
// In GL, submission happens automatically in Run().
// When !triggerFence, we notify after syncing with Vulkan.
if (useThread_ && triggerFence) {
VLOG("PULL: Frame %d.readyForFence = true", frame);
std::unique_lock<std::mutex> lock(frameData.push_mutex);
assert(frameData.readyForSubmit);
frameData.readyForFence = true;
frameData.readyForSubmit = false;
frameData.push_condVar.notify_all();
}
}
void GLRenderManager::EndSubmitFrame(int frame) {
FrameData &frameData = frameData_[frame];
frameData.hasBegun = false;
Submit(frame, true);
if (!frameData.skipSwap) {
if (swapFunction_) {
swapFunction_();
}
} else {
frameData.skipSwap = false;
}
}
void GLRenderManager::Run(int frame) {
BeginSubmitFrame(frame);
FrameData &frameData = frameData_[frame];
// Delete stuff from the last round.
frameData.deleter_prev.Perform();
auto &stepsOnThread = frameData_[frame].steps;
auto &initStepsOnThread = frameData_[frame].initSteps;
// queueRunner_.LogSteps(stepsOnThread);
queueRunner_.RunInitSteps(initStepsOnThread);
queueRunner_.RunSteps(stepsOnThread);
stepsOnThread.clear();
initStepsOnThread.clear();
switch (frameData.type) {
case GLRRunType::END:
frameData.deleter_prev.Take(frameData.deleter);
EndSubmitFrame(frame);
break;
case GLRRunType::SYNC:
EndSyncFrame(frame);
break;
default:
assert(false);
}
VLOG("PULL: Finished running frame %d", frame);
}
void GLRenderManager::FlushSync() {
// Need to flush any pushbuffers to VRAM before submitting draw calls.
for (auto iter : frameData_[curFrame_].activePushBuffers) {
iter->Flush();
}
// TODO: Reset curRenderStep_?
int curFrame = curFrame_;
FrameData &frameData = frameData_[curFrame];
if (!useThread_) {
frameData.initSteps = std::move(initSteps_);
initSteps_.clear();
frameData.steps = std::move(steps_);
steps_.clear();
frameData.type = GLRRunType::SYNC;
Run(curFrame);
} else {
std::unique_lock<std::mutex> lock(frameData.pull_mutex);
VLOG("PUSH: Frame[%d].readyForRun = true (sync)", curFrame);
frameData.initSteps = std::move(initSteps_);
initSteps_.clear();
frameData.steps = std::move(steps_);
steps_.clear();
frameData.readyForRun = true;
assert(frameData.readyForFence == false);
frameData.type = GLRRunType::SYNC;
frameData.pull_condVar.notify_all();
}
if (useThread_) {
std::unique_lock<std::mutex> lock(frameData.push_mutex);
// Wait for the flush to be hit, since we're syncing.
while (!frameData.readyForFence) {
VLOG("PUSH: Waiting for frame[%d].readyForFence = 1 (sync)", curFrame);
frameData.push_condVar.wait(lock);
}
frameData.readyForFence = false;
frameData.readyForSubmit = true;
}
}
void GLRenderManager::EndSyncFrame(int frame) {
FrameData &frameData = frameData_[frame];
Submit(frame, false);
// This is brutal! Should probably wait for a fence instead, not that it'll matter much since we'll
// still stall everything.
glFinish();
// At this point we can resume filling the command buffers for the current frame since
// we know the device is idle - and thus all previously enqueued command buffers have been processed.
// No need to switch to the next frame number.
if (useThread_) {
std::unique_lock<std::mutex> lock(frameData.push_mutex);
frameData.readyForFence = true;
frameData.readyForSubmit = true;
frameData.push_condVar.notify_all();
}
}
void GLRenderManager::Wipe() {
initSteps_.clear();
for (auto step : steps_) {
delete step;
}
steps_.clear();
}
void GLRenderManager::WaitUntilQueueIdle() {
if (!useThread_) {
// Must be idle, nothing to do.
return;
}
// Just wait for all frames to be ready.
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
FrameData &frameData = frameData_[i];
std::unique_lock<std::mutex> lock(frameData.push_mutex);
// Ignore unsubmitted frames.
while (!frameData.readyForFence && !frameData.readyForSubmit) {
VLOG("PUSH: Waiting for frame[%d].readyForFence = 1 (wait idle)", i);
frameData.push_condVar.wait(lock);
}
}
}
GLPushBuffer::GLPushBuffer(GLRenderManager *render, GLuint target, size_t size) : render_(render), target_(target), size_(size) {
bool res = AddBuffer();
assert(res);
}
GLPushBuffer::~GLPushBuffer() {
assert(buffers_.empty());
}
void GLPushBuffer::Map() {
assert(!writePtr_);
// TODO: Even a good old glMapBuffer could actually work well here.
// VkResult res = vkMapMemory(device_, buffers_[buf_].deviceMemory, 0, size_, 0, (void **)(&writePtr_));
writePtr_ = buffers_[buf_].deviceMemory;
assert(writePtr_);
}
void GLPushBuffer::Unmap() {
assert(writePtr_);
// Here we should simply upload everything to the buffers.
// Might be worth trying with size_ instead of offset_, so the driver can replace the whole buffer.
// At least if it's close.
render_->BufferSubdata(buffers_[buf_].buffer, 0, offset_, buffers_[buf_].deviceMemory, false);
writePtr_ = nullptr;
}
void GLPushBuffer::Flush() {
render_->BufferSubdata(buffers_[buf_].buffer, 0, offset_, buffers_[buf_].deviceMemory, false);
// Here we will submit all the draw calls, with the already known buffer and offsets.
// Might as well reset the write pointer here and start over the current buffer.
writePtr_ = buffers_[buf_].deviceMemory;
offset_ = 0;
}
bool GLPushBuffer::AddBuffer() {
BufInfo info;
info.deviceMemory = new uint8_t[size_];
info.buffer = render_->CreateBuffer(target_, size_, GL_DYNAMIC_DRAW);
buf_ = buffers_.size();
buffers_.resize(buf_ + 1);
buffers_[buf_] = info;
return true;
}
void GLPushBuffer::Destroy() {
for (BufInfo &info : buffers_) {
render_->DeleteBuffer(info.buffer);
delete[] info.deviceMemory;
}
buffers_.clear();
}
void GLPushBuffer::NextBuffer(size_t minSize) {
// First, unmap the current memory.
Unmap();
buf_++;
if (buf_ >= buffers_.size() || minSize > size_) {
// Before creating the buffer, adjust to the new size_ if necessary.
while (size_ < minSize) {
size_ <<= 1;
}
bool res = AddBuffer();
assert(res);
if (!res) {
// Let's try not to crash at least?
buf_ = 0;
}
}
// Now, move to the next buffer and map it.
offset_ = 0;
Map();
}
void GLPushBuffer::Defragment() {
if (buffers_.size() <= 1) {
return;
}
// Okay, we have more than one. Destroy them all and start over with a larger one.
size_t newSize = size_ * buffers_.size();
Destroy();
size_ = newSize;
bool res = AddBuffer();
assert(res);
}
size_t GLPushBuffer::GetTotalSize() const {
size_t sum = 0;
if (buffers_.size() > 1)
sum += size_ * (buffers_.size() - 1);
sum += offset_;
return sum;
}
+842
View File
@@ -0,0 +1,842 @@
#pragma once
#include <thread>
#include <unordered_map>
#include <vector>
#include <functional>
#include <set>
#include <string>
#include <mutex>
#include <condition_variable>
#include <cassert>
#include "gfx/gl_common.h"
#include "math/dataconv.h"
#include "Common/Log.h"
#include "GLQueueRunner.h"
class GLRInputLayout;
class GLPushBuffer;
class GLRTexture {
public:
~GLRTexture() {
if (texture) {
glDeleteTextures(1, &texture);
}
}
GLuint texture = 0;
// Could also trust OpenGL defaults I guess..
GLenum target = 0xFFFF;
GLenum wrapS = 0xFFFF;
GLenum wrapT = 0xFFFF;
GLenum magFilter = 0xFFFF;
GLenum minFilter = 0xFFFF;
float anisotropy = -100000.0f;
float minLod = -100000.0f;
float maxLod = 100000.0f;
float lodBias = 0.0f;
};
class GLRFramebuffer {
public:
GLRFramebuffer(int _width, int _height, bool z_stencil)
: width(_width), height(_height), z_stencil_(z_stencil) {
}
~GLRFramebuffer();
int numShadows = 1; // TODO: Support this.
GLuint handle = 0;
GLRTexture color_texture;
GLuint z_stencil_buffer = 0; // Either this is set, or the two below.
GLuint z_buffer = 0;
GLuint stencil_buffer = 0;
int width;
int height;
GLuint colorDepth;
bool z_stencil_;
};
// We need to create some custom heap-allocated types so we can forward things that need to be created on the GL thread, before
// they've actually been created.
class GLRShader {
public:
~GLRShader() {
if (shader) {
glDeleteShader(shader);
}
}
GLuint shader = 0;
bool valid = false;
// Warning: Won't know until a future frame.
bool failed = false;
std::string desc;
std::string code;
};
class GLRProgram {
public:
~GLRProgram() {
if (program) {
glDeleteProgram(program);
}
}
struct Semantic {
int location;
const char *attrib;
};
struct UniformLocQuery {
GLint *dest;
const char *name;
};
struct Initializer {
GLint *uniform;
int type;
int value;
};
GLuint program = 0;
std::vector<Semantic> semantics_;
std::vector<UniformLocQuery> queries_;
std::vector<Initializer> initialize_;
struct UniformInfo {
int loc_;
};
// Must ONLY be called from GLQueueRunner!
// Also it's pretty slow...
int GetUniformLoc(const char *name) {
auto iter = uniformCache_.find(std::string(name));
int loc = -1;
if (iter != uniformCache_.end()) {
loc = iter->second.loc_;
} else {
loc = glGetUniformLocation(program, name);
UniformInfo info;
info.loc_ = loc;
uniformCache_[name] = info;
}
return loc;
}
std::unordered_map<std::string, UniformInfo> uniformCache_;
};
class GLRBuffer {
public:
GLRBuffer(GLuint target, size_t size) : target_(target), size_((int)size) {}
~GLRBuffer() {
if (buffer) {
glDeleteBuffers(1, &buffer);
}
}
GLuint buffer = 0;
GLuint target_;
int size_;
private:
};
enum class GLRRunType {
END,
SYNC,
};
// Synchronize this externally if needed, no mutex anymore.
class GLDeleter {
public:
void Perform();
void Take(GLDeleter &other) {
_assert_msg_(G3D, shaders.empty() && programs.empty() && buffers.empty() && textures.empty() && inputLayouts.empty() && framebuffers.empty(), "Deleter already has stuff");
shaders = std::move(other.shaders);
programs = std::move(other.programs);
buffers = std::move(other.buffers);
textures = std::move(other.textures);
inputLayouts = std::move(other.inputLayouts);
framebuffers = std::move(other.framebuffers);
other.shaders.clear();
other.programs.clear();
other.buffers.clear();
other.textures.clear();
other.inputLayouts.clear();
other.framebuffers.clear();
}
std::vector<GLRShader *> shaders;
std::vector<GLRProgram *> programs;
std::vector<GLRBuffer *> buffers;
std::vector<GLRTexture *> textures;
std::vector<GLRInputLayout *> inputLayouts;
std::vector<GLRFramebuffer *> framebuffers;
};
class GLRInputLayout {
public:
struct Entry {
int location;
int count;
GLenum type;
GLboolean normalized;
int stride;
intptr_t offset;
};
std::vector<Entry> entries;
int semanticsMask_ = 0;
};
class GLRenderManager {
public:
GLRenderManager();
~GLRenderManager();
void ThreadStart();
void ThreadEnd();
bool ThreadFrame(); // Returns false to request exiting the loop.
// Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again.
void BeginFrame();
// Can run on a different thread!
void Finish();
void Run(int frame);
// Zaps queued up commands. Use if you know there's a risk you've queued up stuff that has already been deleted. Can happen during in-game shutdown.
void Wipe();
// Wait until no frames are pending. Use during shutdown before freeing pointers.
void WaitUntilQueueIdle();
// Creation commands. These were not needed in Vulkan since there we can do that on the main thread.
GLRTexture *CreateTexture(GLenum target) {
GLRInitStep step{ GLRInitStepType::CREATE_TEXTURE };
step.create_texture.texture = new GLRTexture();
step.create_texture.texture->target = target;
initSteps_.push_back(step);
return step.create_texture.texture;
}
GLRBuffer *CreateBuffer(GLuint target, size_t size, GLuint usage) {
GLRInitStep step{ GLRInitStepType::CREATE_BUFFER };
step.create_buffer.buffer = new GLRBuffer(target, size);
step.create_buffer.size = (int)size;
step.create_buffer.usage = usage;
initSteps_.push_back(step);
return step.create_buffer.buffer;
}
GLRShader *CreateShader(GLuint stage, const std::string &code, const std::string &desc) {
GLRInitStep step{ GLRInitStepType::CREATE_SHADER };
step.create_shader.shader = new GLRShader();
step.create_shader.shader->desc = desc;
step.create_shader.stage = stage;
step.create_shader.code = new char[code.size() + 1];
memcpy(step.create_shader.code, code.data(), code.size() + 1);
initSteps_.push_back(step);
return step.create_shader.shader;
}
GLRFramebuffer *CreateFramebuffer(int width, int height, bool z_stencil) {
GLRInitStep step{ GLRInitStepType::CREATE_FRAMEBUFFER };
step.create_framebuffer.framebuffer = new GLRFramebuffer(width, height, z_stencil);
initSteps_.push_back(step);
return step.create_framebuffer.framebuffer;
}
// Can't replace uniform initializers with direct calls to SetUniform() etc because there might
// not be an active render pass.
GLRProgram *CreateProgram(
std::vector<GLRShader *> shaders, std::vector<GLRProgram::Semantic> semantics, std::vector<GLRProgram::UniformLocQuery> queries,
std::vector<GLRProgram::Initializer> initalizers, bool supportDualSource) {
GLRInitStep step{ GLRInitStepType::CREATE_PROGRAM };
assert(shaders.size() <= ARRAY_SIZE(step.create_program.shaders));
step.create_program.program = new GLRProgram();
step.create_program.program->semantics_ = semantics;
step.create_program.program->queries_ = queries;
step.create_program.program->initialize_ = initalizers;
step.create_program.support_dual_source = supportDualSource;
_assert_msg_(G3D, shaders.size() > 0, "Can't create a program with zero shaders");
for (int i = 0; i < shaders.size(); i++) {
step.create_program.shaders[i] = shaders[i];
}
#ifdef _DEBUG
for (auto &iter : queries) {
assert(iter.name);
}
for (auto &sem : semantics) {
assert(sem.attrib);
}
#endif
step.create_program.num_shaders = (int)shaders.size();
initSteps_.push_back(step);
return step.create_program.program;
}
GLRInputLayout *CreateInputLayout(std::vector<GLRInputLayout::Entry> &entries) {
GLRInitStep step{ GLRInitStepType::CREATE_INPUT_LAYOUT };
step.create_input_layout.inputLayout = new GLRInputLayout();
step.create_input_layout.inputLayout->entries = entries;
for (auto &iter : step.create_input_layout.inputLayout->entries) {
step.create_input_layout.inputLayout->semanticsMask_ |= 1 << iter.location;
}
initSteps_.push_back(step);
return step.create_input_layout.inputLayout;
}
void DeleteShader(GLRShader *shader) {
frameData_[curFrame_].deleter.shaders.push_back(shader);
}
void DeleteProgram(GLRProgram *program) {
frameData_[curFrame_].deleter.programs.push_back(program);
}
void DeleteBuffer(GLRBuffer *buffer) {
frameData_[curFrame_].deleter.buffers.push_back(buffer);
}
void DeleteTexture(GLRTexture *texture) {
frameData_[curFrame_].deleter.textures.push_back(texture);
}
void DeleteInputLayout(GLRInputLayout *inputLayout) {
frameData_[curFrame_].deleter.inputLayouts.push_back(inputLayout);
}
void DeleteFramebuffer(GLRFramebuffer *framebuffer) {
frameData_[curFrame_].deleter.framebuffers.push_back(framebuffer);
}
void BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, GLRRenderPassAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil);
void BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit, int attachment);
bool CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride);
void CopyImageToMemorySync(GLRTexture *texture, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride);
void CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLOffset2D dstPos, int aspectMask);
void BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLRect2D dstRect, int aspectMask, bool filter);
// Takes ownership of data if deleteData = true.
void BufferSubdata(GLRBuffer *buffer, size_t offset, size_t size, uint8_t *data, bool deleteData = true) {
// TODO: Maybe should be a render command instead of an init command? When possible it's better as
// an init command, that's for sure.
GLRInitStep step{ GLRInitStepType::BUFFER_SUBDATA };
_dbg_assert_(G3D, offset >= 0);
_dbg_assert_(G3D, offset <= buffer->size_ - size);
step.buffer_subdata.buffer = buffer;
step.buffer_subdata.offset = (int)offset;
step.buffer_subdata.size = (int)size;
step.buffer_subdata.data = data;
step.buffer_subdata.deleteData = deleteData;
initSteps_.push_back(step);
}
// Takes ownership over the data pointer and delete[]-s it.
void TextureImage(GLRTexture *texture, int level, int width, int height, GLenum internalFormat, GLenum format, GLenum type, uint8_t *data, bool linearFilter = false) {
GLRInitStep step{ GLRInitStepType::TEXTURE_IMAGE };
step.texture_image.texture = texture;
step.texture_image.data = data;
step.texture_image.internalFormat = internalFormat;
step.texture_image.format = format;
step.texture_image.type = type;
step.texture_image.level = level;
step.texture_image.width = width;
step.texture_image.height = height;
step.texture_image.linearFilter = linearFilter;
initSteps_.push_back(step);
}
void FinalizeTexture(GLRTexture *texture, int maxLevels, bool genMips) {
GLRInitStep step{ GLRInitStepType::TEXTURE_FINALIZE };
step.texture_finalize.texture = texture;
step.texture_finalize.maxLevel = maxLevels;
step.texture_finalize.genMips = genMips;
initSteps_.push_back(step);
}
void BindTexture(int slot, GLRTexture *tex) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::BINDTEXTURE };
_dbg_assert_(G3D, slot < 16);
data.texture.slot = slot;
data.texture.texture = tex;
curRenderStep_->commands.push_back(data);
}
void BindProgram(GLRProgram *program) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::BINDPROGRAM };
_dbg_assert_(G3D, program != nullptr);
data.program.program = program;
curRenderStep_->commands.push_back(data);
}
void BindPixelPackBuffer(GLRBuffer *buffer) { // Want to support an offset but can't in ES 2.0. We supply an offset when binding the buffers instead.
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::BIND_BUFFER };
data.bind_buffer.buffer = buffer;
data.bind_buffer.target = GL_PIXEL_PACK_BUFFER;
curRenderStep_->commands.push_back(data);
}
void BindIndexBuffer(GLRBuffer *buffer) { // Want to support an offset but can't in ES 2.0. We supply an offset when binding the buffers instead.
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::BIND_BUFFER};
data.bind_buffer.buffer = buffer;
data.bind_buffer.target = GL_ELEMENT_ARRAY_BUFFER;
curRenderStep_->commands.push_back(data);
}
void BindVertexBuffer(GLRInputLayout *inputLayout, GLRBuffer *buffer, size_t offset) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
assert(inputLayout);
GLRRenderData data{ GLRRenderCommand::BIND_VERTEX_BUFFER };
data.bindVertexBuffer.inputLayout = inputLayout;
data.bindVertexBuffer.offset = offset;
data.bindVertexBuffer.buffer = buffer;
curRenderStep_->commands.push_back(data);
}
void SetDepth(bool enabled, bool write, GLenum func) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::DEPTH };
data.depth.enabled = enabled;
data.depth.write = write;
data.depth.func = func;
curRenderStep_->commands.push_back(data);
}
void SetViewport(const GLRViewport &vp) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::VIEWPORT };
data.viewport.vp = vp;
curRenderStep_->commands.push_back(data);
}
void SetScissor(const GLRect2D &rc) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::SCISSOR };
data.scissor.rc = rc;
curRenderStep_->commands.push_back(data);
}
void SetUniformI(GLint *loc, int count, const int *udata) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::UNIFORM4I };
data.uniform4.loc = loc;
data.uniform4.count = count;
memcpy(data.uniform4.v, udata, sizeof(int) * count);
curRenderStep_->commands.push_back(data);
}
void SetUniformI1(GLint *loc, int udata) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::UNIFORM4I };
data.uniform4.loc = loc;
data.uniform4.count = 1;
memcpy(data.uniform4.v, &udata, sizeof(udata));
curRenderStep_->commands.push_back(data);
}
void SetUniformF(GLint *loc, int count, const float *udata) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::UNIFORM4F };
data.uniform4.loc = loc;
data.uniform4.count = count;
memcpy(data.uniform4.v, udata, sizeof(float) * count);
curRenderStep_->commands.push_back(data);
}
void SetUniformF1(GLint *loc, const float udata) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::UNIFORM4F };
data.uniform4.loc = loc;
data.uniform4.count = 1;
memcpy(data.uniform4.v, &udata, sizeof(float));
curRenderStep_->commands.push_back(data);
}
void SetUniformF(const char *name, int count, const float *udata) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::UNIFORM4F };
data.uniform4.name = name;
data.uniform4.count = count;
memcpy(data.uniform4.v, udata, sizeof(float) * count);
curRenderStep_->commands.push_back(data);
}
void SetUniformM4x4(GLint *loc, const float *udata) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::UNIFORMMATRIX };
data.uniformMatrix4.loc = loc;
memcpy(data.uniformMatrix4.m, udata, sizeof(float) * 16);
curRenderStep_->commands.push_back(data);
}
void SetUniformM4x4(const char *name, const float *udata) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::UNIFORMMATRIX };
data.uniformMatrix4.name = name;
memcpy(data.uniformMatrix4.m, udata, sizeof(float) * 16);
curRenderStep_->commands.push_back(data);
}
void SetBlendAndMask(int colorMask, bool blendEnabled, GLenum srcColor, GLenum dstColor, GLenum srcAlpha, GLenum dstAlpha, GLenum funcColor, GLenum funcAlpha) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::BLEND };
data.blend.mask = colorMask;
data.blend.enabled = blendEnabled;
data.blend.srcColor = srcColor;
data.blend.dstColor = dstColor;
data.blend.srcAlpha = srcAlpha;
data.blend.dstAlpha = dstAlpha;
data.blend.funcColor = funcColor;
data.blend.funcAlpha = funcAlpha;
curRenderStep_->commands.push_back(data);
}
void SetNoBlendAndMask(int colorMask) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::BLEND };
data.blend.mask = colorMask;
data.blend.enabled = false;
curRenderStep_->commands.push_back(data);
}
#ifndef USING_GLES2
void SetLogicOp(bool enabled, GLenum logicOp) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::LOGICOP };
data.logic.enabled = enabled;
data.logic.logicOp = logicOp;
curRenderStep_->commands.push_back(data);
}
#endif
void SetStencilFunc(bool enabled, GLenum func, uint8_t refValue, uint8_t compareMask) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::STENCILFUNC };
data.stencilFunc.enabled = enabled;
data.stencilFunc.func = func;
data.stencilFunc.ref = refValue;
data.stencilFunc.compareMask = compareMask;
curRenderStep_->commands.push_back(data);
}
void SetStencilOp(uint8_t writeMask, GLenum sFail, GLenum zFail, GLenum pass) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::STENCILOP };
data.stencilOp.writeMask = writeMask;
data.stencilOp.sFail = sFail;
data.stencilOp.zFail = zFail;
data.stencilOp.pass = pass;
curRenderStep_->commands.push_back(data);
}
void SetStencilDisabled() {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data;
data.cmd = GLRRenderCommand::STENCILFUNC;
data.stencilFunc.enabled = false;
curRenderStep_->commands.push_back(data);
}
void SetBlendFactor(const float color[4]) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::BLENDCOLOR };
CopyFloat4(data.blendColor.color, color);
curRenderStep_->commands.push_back(data);
}
void SetRaster(GLboolean cullEnable, GLenum frontFace, GLenum cullFace, GLboolean ditherEnable) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::RASTER };
data.raster.cullEnable = cullEnable;
data.raster.frontFace = frontFace;
data.raster.cullFace = cullFace;
data.raster.ditherEnable = ditherEnable;
curRenderStep_->commands.push_back(data);
}
// Modifies the current texture as per GL specs, not global state.
void SetTextureSampler(GLenum wrapS, GLenum wrapT, GLenum magFilter, GLenum minFilter, float anisotropy) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::TEXTURESAMPLER };
data.textureSampler.wrapS = wrapS;
data.textureSampler.wrapT = wrapT;
data.textureSampler.magFilter = magFilter;
data.textureSampler.minFilter = minFilter;
data.textureSampler.anisotropy = anisotropy;
curRenderStep_->commands.push_back(data);
}
void SetTextureLod(float minLod, float maxLod, float lodBias) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::TEXTURELOD};
data.textureLod.minLod = minLod;
data.textureLod.maxLod = maxLod;
data.textureLod.lodBias = lodBias;
curRenderStep_->commands.push_back(data);
}
void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask, int colorMask = 0xF) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::CLEAR };
data.clear.clearMask = clearMask;
data.clear.clearColor = clearColor;
data.clear.clearZ = clearZ;
data.clear.clearStencil = clearStencil;
data.clear.colorMask = colorMask;
curRenderStep_->commands.push_back(data);
}
void Invalidate(int invalidateMask) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::INVALIDATE };
data.clear.clearMask = invalidateMask;
curRenderStep_->commands.push_back(data);
}
void Draw(GLenum mode, int first, int count) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::DRAW };
data.draw.mode = mode;
data.draw.first = first;
data.draw.count = count;
data.draw.buffer = 0;
curRenderStep_->commands.push_back(data);
curRenderStep_->render.numDraws++;
}
void DrawIndexed(GLenum mode, int count, GLenum indexType, void *indices, int instances = 1) {
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
GLRRenderData data{ GLRRenderCommand::DRAW_INDEXED };
data.drawIndexed.mode = mode;
data.drawIndexed.count = count;
data.drawIndexed.indexType = indexType;
data.drawIndexed.instances = instances;
data.drawIndexed.indices = indices;
curRenderStep_->commands.push_back(data);
curRenderStep_->render.numDraws++;
}
enum { MAX_INFLIGHT_FRAMES = 3 };
int GetCurFrame() const {
return curFrame_;
}
void Resize(int width, int height) {
targetWidth_ = width;
targetHeight_ = height;
queueRunner_.Resize(width, height);
}
// When using legacy functionality for push buffers (glBufferData), we need to flush them
// before actually making the glDraw* calls. It's best if the render manager handles that.
void RegisterPushBuffer(int frame, GLPushBuffer *buffer) {
frameData_[frame].activePushBuffers.insert(buffer);
}
void UnregisterPushBuffer(int frame, GLPushBuffer *buffer) {
auto iter = frameData_[frame].activePushBuffers.find(buffer);
_assert_(iter != frameData_[frame].activePushBuffers.end());
frameData_[frame].activePushBuffers.erase(iter);
}
void SetSwapFunction(std::function<void()> swapFunction) {
swapFunction_ = swapFunction;
}
void Swap() {
if (!useThread_ && swapFunction_) {
swapFunction_();
}
}
void StopThread();
bool SawOutOfMemory() {
return queueRunner_.SawOutOfMemory();
}
// Only supports a common subset.
std::string GetGLString(int name) const {
return queueRunner_.GetGLString(name);
}
private:
void BeginSubmitFrame(int frame);
void EndSubmitFrame(int frame);
void Submit(int frame, bool triggerFence);
// Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot).
void FlushSync();
void EndSyncFrame(int frame);
// Per-frame data, round-robin so we can overlap submission with execution of the previous frame.
struct FrameData {
std::mutex push_mutex;
std::condition_variable push_condVar;
std::mutex pull_mutex;
std::condition_variable pull_condVar;
bool readyForFence = true;
bool readyForRun = false;
bool readyForSubmit = false;
bool skipSwap = false;
GLRRunType type = GLRRunType::END;
// GLuint fence; For future AZDO stuff?
std::vector<GLRStep *> steps;
std::vector<GLRInitStep> initSteps;
// Swapchain.
bool hasBegun = false;
uint32_t curSwapchainImage = -1;
std::mutex deleter_mutex;
GLDeleter deleter;
GLDeleter deleter_prev;
std::set<GLPushBuffer *> activePushBuffers;
};
FrameData frameData_[MAX_INFLIGHT_FRAMES];
// Submission time state
bool insideFrame_ = false;
GLRStep *curRenderStep_ = nullptr;
std::vector<GLRStep *> steps_;
std::vector<GLRInitStep> initSteps_;
// Execution time state
bool run_ = true;
// Thread is managed elsewhere, and should call ThreadFrame.
std::mutex mutex_;
int threadInitFrame_ = 0;
GLQueueRunner queueRunner_;
// Thread state
int threadFrame_;
bool nextFrame = false;
bool firstFrame = true;
bool useThread_ = true;
int curFrame_ = 0;
std::function<void()> swapFunction_;
int targetWidth_ = 0;
int targetHeight_ = 0;
};
// Similar to VulkanPushBuffer but uses really stupid tactics - collect all the data in RAM then do a big
// memcpy/buffer upload at the end. This can however be optimized with glBufferStorage on chips that support that
// for massive boosts.
class GLPushBuffer {
public:
struct BufInfo {
GLRBuffer *buffer;
uint8_t *deviceMemory;
};
public:
GLPushBuffer(GLRenderManager *render, GLuint target, size_t size);
~GLPushBuffer();
void Destroy();
void Reset() { offset_ = 0; }
// Needs context in case of defragment.
void Begin() {
buf_ = 0;
offset_ = 0;
// Note: we must defrag because some buffers may be smaller than size_.
Defragment();
Map();
assert(writePtr_);
}
void BeginNoReset() {
Map();
}
void End() {
Unmap();
}
void Map();
void Unmap();
// When using the returned memory, make sure to bind the returned vkbuf.
// This will later allow for handling overflow correctly.
size_t Allocate(size_t numBytes, GLRBuffer **vkbuf) {
size_t out = offset_;
if (offset_ + ((numBytes + 3) & ~3) >= size_) {
NextBuffer(numBytes);
out = offset_;
offset_ += (numBytes + 3) & ~3;
} else {
offset_ += (numBytes + 3) & ~3; // Round up to 4 bytes.
}
*vkbuf = buffers_[buf_].buffer;
return out;
}
// Returns the offset that should be used when binding this buffer to get this data.
size_t Push(const void *data, size_t size, GLRBuffer **vkbuf) {
assert(writePtr_);
size_t off = Allocate(size, vkbuf);
memcpy(writePtr_ + off, data, size);
return off;
}
uint32_t PushAligned(const void *data, size_t size, int align, GLRBuffer **vkbuf) {
assert(writePtr_);
offset_ = (offset_ + align - 1) & ~(align - 1);
size_t off = Allocate(size, vkbuf);
memcpy(writePtr_ + off, data, size);
return (uint32_t)off;
}
size_t GetOffset() const {
return offset_;
}
// "Zero-copy" variant - you can write the data directly as you compute it.
// Recommended.
void *Push(size_t size, uint32_t *bindOffset, GLRBuffer **vkbuf) {
assert(writePtr_);
size_t off = Allocate(size, vkbuf);
*bindOffset = (uint32_t)off;
return writePtr_ + off;
}
void *PushAligned(size_t size, uint32_t *bindOffset, GLRBuffer **vkbuf, int align) {
assert(writePtr_);
offset_ = (offset_ + align - 1) & ~(align - 1);
size_t off = Allocate(size, vkbuf);
*bindOffset = (uint32_t)off;
return writePtr_ + off;
}
size_t GetTotalSize() const;
void Flush();
private:
bool AddBuffer();
void NextBuffer(size_t minSize);
void Defragment();
GLRenderManager *render_;
std::vector<BufInfo> buffers_;
size_t buf_ = 0;
size_t offset_ = 0;
size_t size_ = 0;
uint8_t *writePtr_ = nullptr;
GLuint target_;
};
+3 -3
View File
@@ -391,7 +391,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
}
// Don't execute empty renderpasses.
if (step.commands.empty() && step.render.color == VKRRenderPassAction::KEEP && step.render.depthStencil == VKRRenderPassAction::KEEP) {
if (step.commands.empty() && step.render.color == VKRRenderPassAction::KEEP && step.render.depth == VKRRenderPassAction::KEEP && step.render.stencil == VKRRenderPassAction::KEEP) {
// Nothing to do.
return;
}
@@ -610,12 +610,12 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step
fb->depth.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
renderPass = GetRenderPass(step.render.color, step.render.depthStencil, step.render.depthStencil);
renderPass = GetRenderPass(step.render.color, step.render.depth, step.render.stencil);
if (step.render.color == VKRRenderPassAction::CLEAR) {
Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor);
numClearVals = 1;
}
if (step.render.depthStencil == VKRRenderPassAction::CLEAR) {
if (step.render.depth == VKRRenderPassAction::CLEAR || step.render.stencil == VKRRenderPassAction::CLEAR) {
clearVal[1].depthStencil.depth = step.render.clearDepth;
clearVal[1].depthStencil.stencil = step.render.clearStencil;
numClearVals = 2;
+2 -1
View File
@@ -108,7 +108,8 @@ struct VKRStep {
struct {
VKRFramebuffer *framebuffer;
VKRRenderPassAction color;
VKRRenderPassAction depthStencil;
VKRRenderPassAction depth;
VKRRenderPassAction stencil;
uint32_t clearColor;
float clearDepth;
int clearStencil;
+7 -5
View File
@@ -389,16 +389,16 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() {
return frameData_[curFrame].initCmd;
}
void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil) {
void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, VKRRenderPassAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil) {
assert(insideFrame_);
// Eliminate dupes.
if (steps_.size() && steps_.back()->render.framebuffer == fb && steps_.back()->stepType == VKRStepType::RENDER) {
if (color != VKRRenderPassAction::CLEAR && depth != VKRRenderPassAction::CLEAR) {
if (color != VKRRenderPassAction::CLEAR && depth != VKRRenderPassAction::CLEAR && stencil != VKRRenderPassAction::CLEAR) {
// We don't move to a new step, this bind was unnecessary and we can safely skip it.
return;
}
}
if (curRenderStep_ && curRenderStep_->commands.size() == 0 && curRenderStep_->render.color == VKRRenderPassAction::KEEP && curRenderStep_->render.depthStencil == VKRRenderPassAction::KEEP) {
if (curRenderStep_ && curRenderStep_->commands.size() == 0 && curRenderStep_->render.color == VKRRenderPassAction::KEEP && curRenderStep_->render.depth == VKRRenderPassAction::KEEP && curRenderStep_->render.stencil == VKRRenderPassAction::KEEP) {
// Can trivially kill the last empty render step.
assert(steps_.back() == curRenderStep_);
delete steps_.back();
@@ -413,7 +413,8 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
// This is what queues up new passes, and can end previous ones.
step->render.framebuffer = fb;
step->render.color = color;
step->render.depthStencil = depth;
step->render.depth= depth;
step->render.stencil = stencil;
step->render.clearColor = clearColor;
step->render.clearDepth = clearDepth;
step->render.clearStencil = clearStencil;
@@ -617,7 +618,8 @@ void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearSten
curRenderStep_->render.clearDepth = clearZ;
curRenderStep_->render.clearStencil = clearStencil;
curRenderStep_->render.color = (clearMask & VK_IMAGE_ASPECT_COLOR_BIT) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP;
curRenderStep_->render.depthStencil = (clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP;
curRenderStep_->render.depth = (clearMask & VK_IMAGE_ASPECT_DEPTH_BIT) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP;
curRenderStep_->render.stencil = (clearMask & VK_IMAGE_ASPECT_STENCIL_BIT) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP;
} else {
VkRenderData data{ VKRRenderCommand::CLEAR };
data.clear.clearColor = clearColor;
+1 -1
View File
@@ -99,7 +99,7 @@ public:
// Zaps queued up commands. Use if you know there's a risk you've queued up stuff that has already been deleted. Can happen during in-game shutdown.
void Wipe();
void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil);
void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, VKRRenderPassAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil);
VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment);
bool CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride);
void CopyImageToMemorySync(VkImage image, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride);
+5 -2
View File
@@ -589,7 +589,10 @@ public:
// color must be 0, for now.
virtual void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) = 0;
virtual uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) = 0;
// deprecated
virtual uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) {
return 0;
}
virtual void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) = 0;
@@ -632,7 +635,7 @@ public:
virtual void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) = 0;
// Necessary to correctly flip scissor rectangles etc for OpenGL.
void SetTargetSize(int w, int h) {
virtual void SetTargetSize(int w, int h) {
targetWidth_ = w;
targetHeight_ = h;
}
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -1338,8 +1338,9 @@ void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPass
VKFramebuffer *fb = (VKFramebuffer *)fbo;
VKRRenderPassAction color = (VKRRenderPassAction)rp.color;
VKRRenderPassAction depth = (VKRRenderPassAction)rp.depth;
VKRRenderPassAction stencil = (VKRRenderPassAction)rp.stencil;
renderManager_.BindFramebufferAsRenderTarget(fb ? fb->GetFB() : nullptr, color, depth, rp.clearColor, rp.clearDepth, rp.clearStencil);
renderManager_.BindFramebufferAsRenderTarget(fb ? fb->GetFB() : nullptr, color, depth, stencil, rp.clearColor, rp.clearDepth, rp.clearStencil);
curFramebuffer_ = fb;
}
+5 -3
View File
@@ -12,7 +12,7 @@
#include "base/logging.h"
#include "thread/threadutil.h"
#ifdef __ANDROID__
#if defined(__ANDROID__) || defined(__APPLE__) || (defined(__GLIBC__) && defined(_GNU_SOURCE))
#include <pthread.h>
#endif
@@ -96,10 +96,12 @@ void setCurrentThreadName(const char* threadName) {
{}
#else
#if defined(__ANDROID__)
#if defined(__ANDROID__) || (defined(__GLIBC__) && defined(_GNU_SOURCE))
pthread_setname_np(pthread_self(), threadName);
#elif defined(__APPLE__)
pthread_setname_np(threadName);
// #else
// pthread_setname_np(thread_name);
// pthread_setname_np(threadName);
#endif
// Do nothing
+32 -8
View File
@@ -16,6 +16,7 @@
#include "net/resolve.h"
#include "ui/screen.h"
#include "thin3d/thin3d.h"
#include "thin3d/GLRenderManager.h"
#include "input/keycodes.h"
#include "gfx_es2/gpu_features.h"
@@ -30,23 +31,36 @@
#define IS_IPAD() ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
#define IS_IPHONE() ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)
class IOSDummyGraphicsContext : public DummyGraphicsContext {
class IOSGraphicsContext : public DummyGraphicsContext {
public:
IOSDummyGraphicsContext() {
IOSGraphicsContext() {
CheckGLExtensions();
draw_ = Draw::T3DCreateGLContext();
renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
SetGPUBackend(GPUBackend::OPENGL);
bool success = draw_->CreatePresets();
assert(success);
}
~IOSDummyGraphicsContext() {
~IOSGraphicsContext() {
delete draw_;
}
Draw::DrawContext *GetDrawContext() override {
return draw_;
}
void ThreadStart() override {
renderManager_->ThreadStart();
}
bool ThreadFrame() override {
return renderManager_->ThreadFrame();
}
void ThreadEnd() override {
renderManager_->ThreadEnd();
}
private:
Draw::DrawContext *draw_;
GLRenderManager *renderManager_;
};
static float dp_xscale = 1.0f;
@@ -55,6 +69,7 @@ static float dp_yscale = 1.0f;
static double lastSelectPress = 0.0f;
static double lastStartPress = 0.0f;
static bool simulateAnalog = false;
static bool threadEnabled = true;
__unsafe_unretained static ViewController* sharedViewController;
static GraphicsContext *graphicsContext;
@@ -153,9 +168,10 @@ static GraphicsContext *graphicsContext;
pixel_in_dps_x = (float)pixel_xres / (float)dp_xres;
pixel_in_dps_y = (float)pixel_yres / (float)dp_yres;
graphicsContext = new IOSDummyGraphicsContext();
graphicsContext = new IOSGraphicsContext();
NativeInitGraphics(graphicsContext);
graphicsContext->ThreadStart();
dp_xscale = (float)dp_xres / (float)pixel_xres;
dp_yscale = (float)dp_yres / (float)pixel_yres;
@@ -172,6 +188,14 @@ static GraphicsContext *graphicsContext;
}
}
#endif
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
while (threadEnabled) {
NativeUpdate();
NativeRender(graphicsContext);
time_update();
}
});
}
- (void)didReceiveMemoryWarning
@@ -195,6 +219,8 @@ static GraphicsContext *graphicsContext;
self.gameController = nil;
}
#endif
threadEnabled = false;
graphicsContext->ThreadEnd();
NativeShutdownGraphics();
graphicsContext->Shutdown();
delete graphicsContext;
@@ -216,9 +242,7 @@ static GraphicsContext *graphicsContext;
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
NativeUpdate();
NativeRender(graphicsContext);
time_update();
graphicsContext->ThreadFrame();
}
- (void)touchX:(float)x y:(float)y code:(int)code pointerId:(int)pointerId