From dd91cb0f8e1831fbbe056a7e1a1ee677f5e3493a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 18 Nov 2017 15:42:39 +0100 Subject: [PATCH 01/96] Begin implementing GLQueueRunner and GLRenderManager --- ext/native/native.vcxproj | 4 + ext/native/native.vcxproj.filters | 12 + ext/native/thin3d/GLQueueRunner.cpp | 217 +++++++++++++++++ ext/native/thin3d/GLQueueRunner.h | 260 ++++++++++++++++++++ ext/native/thin3d/GLRenderManager.cpp | 326 ++++++++++++++++++++++++++ ext/native/thin3d/GLRenderManager.h | 297 +++++++++++++++++++++++ ext/native/thin3d/thin3d_gl.cpp | 4 + 7 files changed, 1120 insertions(+) create mode 100644 ext/native/thin3d/GLQueueRunner.cpp create mode 100644 ext/native/thin3d/GLQueueRunner.h create mode 100644 ext/native/thin3d/GLRenderManager.cpp create mode 100644 ext/native/thin3d/GLRenderManager.h diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index 9ccbc1e644..3f520beb66 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -241,6 +241,8 @@ + + @@ -699,6 +701,8 @@ + + diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index bec0f42cee..d452261752 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -332,6 +332,12 @@ base + + thin3d + + + thin3d + @@ -799,6 +805,12 @@ ui + + thin3d + + + thin3d + diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp new file mode 100644 index 0000000000..ff4f90ccba --- /dev/null +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -0,0 +1,217 @@ +#include "GLQueueRunner.h" +#include "GLRenderManager.h" +#include "gfx_es2/gpu_features.h" +#include "math/dataconv.h" + +void GLQueueRunner::CreateDeviceObjects() { + +} + +void GLQueueRunner::DestroyDeviceObjects() { + +} + +void GLQueueRunner::RunInitSteps(const std::vector &steps) { + +} + +void GLQueueRunner::RunSteps(const std::vector &steps) { + for (int i = 0; i < steps.size(); i++) { + const GLRStep &step = *steps[i]; + switch (step.stepType) { + case GLRStepType::RENDER: + PerformRenderPass(step); + break; + case GLRStepType::COPY: + PerformCopy(step); + break; + case GLRStepType::BLIT: + PerformBlit(step); + break; + case GLRStepType::READBACK: + PerformReadback(step); + break; + case GLRStepType::READBACK_IMAGE: + PerformReadbackImage(step); + break; + } + delete steps[i]; + } +} + +void GLQueueRunner::LogSteps(const std::vector &steps) { + +} + + +void GLQueueRunner::PerformBlit(const GLRStep &step) { +} + +void GLQueueRunner::PerformRenderPass(const GLRStep &step) { + // Don't execute empty renderpasses. + if (step.commands.empty() && step.render.color == GLRRenderPassAction::KEEP && step.render.depthStencil == GLRRenderPassAction::KEEP) { + // Nothing to do. + return; + } + + // This is supposed to bind a vulkan render pass to the command buffer. + PerformBindFramebufferAsRenderTarget(step); + + int curWidth = step.render.framebuffer ? step.render.framebuffer->width : 0; // vulkan_->GetBackbufferWidth(); + int curHeight = step.render.framebuffer ? step.render.framebuffer->height : 0; // vulkan_->GetBackbufferHeight(); + + GLRFramebuffer *fb = step.render.framebuffer; + + GLint activeTexture = GL_TEXTURE0; + + auto &commands = step.commands; + for (const auto &c : commands) { + switch (c.cmd) { + case GLRRenderCommand::DEPTH: + if (c.depth.enabled) { + glEnable(GL_DEPTH_TEST); + glDepthMask(c.depth.write); + glDepthFunc(c.depth.func); + } else { + glDisable(GL_DEPTH_TEST); + } + break; + case GLRRenderCommand::BLEND: + if (c.blend.enabled) { + glEnable(GL_BLEND); + glBlendEquationSeparate(c.blend.funcColor, c.blend.funcAlpha); + glBlendFuncSeparate(c.blend.srcColor, c.blend.dstColor, c.blend.srcAlpha, c.blend.dstAlpha); + } else { + glDisable(GL_BLEND); + } + break; + case GLRRenderCommand::CLEAR: + if (c.clear.clearMask & GLR_ASPECT_COLOR) { + float color[4]; + Uint8x4ToFloat4(color, c.clear.clearColor); + glClearColor(color[0], color[1], color[2], color[3]); + } + if (c.clear.clearMask & GLR_ASPECT_DEPTH) { + glClearDepth(c.clear.clearZ); + } + if (c.clear.clearMask & GLR_ASPECT_STENCIL) { + glClearStencil(c.clear.clearStencil); + } + break; + case GLRRenderCommand::BLENDCOLOR: + glBlendColor(c.blendColor.color[0], c.blendColor.color[1], c.blendColor.color[2], c.blendColor.color[3]); + break; + case GLRRenderCommand::VIEWPORT: + // TODO: Support FP viewports through glViewportArrays + glViewport((GLint)c.viewport.vp.x, (GLint)c.viewport.vp.y, (GLsizei)c.viewport.vp.w, (GLsizei)c.viewport.vp.h); + glDepthRange(c.viewport.vp.minZ, c.viewport.vp.maxZ); + break; + case GLRRenderCommand::SCISSOR: + glScissor(c.scissor.rc.x, c.scissor.rc.y, c.scissor.rc.w, c.scissor.rc.h); + break; + case GLRRenderCommand::UNIFORM4F: + switch (c.uniform4.count) { + case 1: + glUniform1f(c.uniform4.loc, c.uniform4.v[0]); + break; + case 2: + glUniform2fv(c.uniform4.loc, 1, c.uniform4.v); + break; + case 3: + glUniform3fv(c.uniform4.loc, 1, c.uniform4.v); + break; + case 4: + glUniform4fv(c.uniform4.loc, 1, c.uniform4.v); + break; + } + break; + case GLRRenderCommand::UNIFORMMATRIX: + glUniformMatrix4fv(c.uniformMatrix4.loc, 1, false, c.uniformMatrix4.m); + break; + case GLRRenderCommand::STENCIL: + glStencilFunc(c.stencil.stencilFunc, c.stencil.stencilRef, c.stencil.stencilCompareMask); + glStencilOp(c.stencil.stencilSFail, c.stencil.stencilZFail, c.stencil.stencilPass); + glStencilMask(c.stencil.stencilWriteMask); + break; + case GLRRenderCommand::BINDTEXTURE: + { + GLint target = c.texture.slot; + if (target != activeTexture) { + glActiveTexture(target); + activeTexture = target; + } + glBindTexture(GL_TEXTURE_2D, c.texture.texture); + break; + } + case GLRRenderCommand::DRAW: + glDrawArrays(c.draw.mode, c.draw.first, c.draw.count); + break; + case GLRRenderCommand::DRAW_INDEXED: + if (c.drawIndexed.instances == 1) { + glDrawElements(c.drawIndexed.mode, c.drawIndexed.count, c.drawIndexed.indexType, c.drawIndexed.indices); + } + break; + } + } + if (activeTexture != GL_TEXTURE0) + glActiveTexture(GL_TEXTURE0); +} + +void GLQueueRunner::PerformCopy(const GLRStep &step) { + GLuint srcTex = 0; + GLuint dstTex = 0; + GLuint target = GL_TEXTURE_2D; + + const GLRect2D &srcRect = step.copy.srcRect; + const GLOffset2D &dstPos = step.copy.dstPos; + + GLRFramebuffer *src = step.copy.src; + GLRFramebuffer *dst = step.copy.src; + + int srcLevel = 0; + int dstLevel = 0; + int srcZ = 0; + int dstZ = 0; + int depth = 1; + + switch (step.copy.aspectMask) { + case GLR_ASPECT_COLOR: + srcTex = src->color.texture; + dstTex = dst->color.texture; + break; + case GLR_ASPECT_DEPTH: + target = GL_RENDERBUFFER; + srcTex = src->depth.texture; + dstTex = src->depth.texture; + break; + } +#if defined(USING_GLES2) +#ifndef IOS + glCopyImageSubDataOES( + srcTex, target, srcLevel, srcRect.x, srcRect.y, srcZ, + dstTex, target, dstLevel, dstPos.x, dstPos.y, dstZ, + srcRect.w, srcRect.h, depth); +#endif +#else + if (gl_extensions.ARB_copy_image) { + glCopyImageSubData( + srcTex, target, srcLevel, srcRect.x, srcRect.y, srcZ, + dstTex, target, dstLevel, dstPos.x, dstPos.y, dstZ, + srcRect.w, srcRect.h, depth); + } else if (gl_extensions.NV_copy_image) { + // Older, pre GL 4.x NVIDIA cards. + glCopyImageSubDataNV( + srcTex, target, srcLevel, srcRect.x, srcRect.y, srcZ, + dstTex, target, dstLevel, dstPos.x, dstPos.y, dstZ, + srcRect.w, srcRect.h, depth); + } +#endif +} + +void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) { + +} + +void GLQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) { + +} diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h new file mode 100644 index 0000000000..a691927a26 --- /dev/null +++ b/ext/native/thin3d/GLQueueRunner.h @@ -0,0 +1,260 @@ +#pragma once + +#include +#include + +#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; +}; + +enum class GLRRenderCommand : uint8_t { + DEPTH, + STENCIL, + BLEND, + BLENDCOLOR, + UNIFORM4F, + UNIFORMMATRIX, + TEXTURESAMPLER, + VIEWPORT, + SCISSOR, + CLEAR, + BINDTEXTURE, + DRAW, + DRAW_INDEXED, + PUSH_CONSTANTS, +}; + +struct GLRRenderData { + GLRRenderCommand cmd; + union { + struct { + GLboolean enabled; + GLboolean write; + GLenum func; + } depth; + struct { + GLboolean enabled; + GLenum stencilOp; + GLenum stencilFunc; + uint8_t stencilWriteMask; + uint8_t stencilCompareMask; + uint8_t stencilRef; + GLenum stencilSFail; + GLenum stencilZFail; + GLenum stencilPass; + } stencil; + 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 { + GLint loc; + GLint count; + float v[4]; + } uniform4; + struct { + GLint loc; + float m[16]; + } uniformMatrix4; + struct { + uint32_t clearColor; + float clearZ; + int clearStencil; + int clearMask; // VK_IMAGE_ASPECT_COLOR_BIT etc + } clear; + struct { + int slot; + GLint texture; + } texture; + struct { + GLuint wrapU; + GLuint wrapV; + bool maxFilter; + bool minFilter; + bool mipFilter; + } textureSampler; + struct { + GLRViewport vp; + } viewport; + struct { + GLRect2D rc; + } scissor; + struct { + GLboolean enabled; + GLenum srcColor; + GLenum dstColor; + GLenum srcAlpha; + GLenum dstAlpha; + GLenum funcColor; + GLenum funcAlpha; + } blend; + struct { + float color[4]; + } blendColor; + }; +}; + +// 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, + + TEXTURE_SUBDATA, + BUFFER_SUBDATA, +}; + +class GLRShader; +class GLRTexture; +class GLRProgram; + +struct GLRInitStep { + GLRInitStep(GLRInitStepType _type) : stepType(_type) {} + GLRInitStepType stepType; + union { + struct { + GLRTexture *texture; + int width; + int height; + // ... + } create_texture; + struct { + GLRShader *shader; + const char *code; + } create_shader; + struct { + GLRProgram *program; + GLRShader *vshader; + GLRShader *fshader; + } create_program; + }; +}; + +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 commands; + union { + struct { + GLRFramebuffer *framebuffer; + GLRRenderPassAction color; + GLRRenderPassAction depthStencil; + uint32_t clearColor; + float clearDepth; + int clearStencil; + 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; + } readback; + struct { + GLint texture; + GLRect2D srcRect; + int mipLevel; + } readback_image; + }; +}; + +class GLQueueRunner { +public: + GLQueueRunner() {} + + void RunInitSteps(const std::vector &steps); + + void RunSteps(const std::vector &steps); + void LogSteps(const std::vector &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); + +private: + 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 ResizeReadbackBuffer(size_t requiredSize); + + GLint curFramebuffer_ = 0; + + // Readback buffer. Currently we only support synchronous readback, so we only really need one. + // We size it generously. + GLint readbackBuffer_ = 0; + int readbackBufferSize_ = 0; +}; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp new file mode 100644 index 0000000000..ba43fb1768 --- /dev/null +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -0,0 +1,326 @@ +#include + +#include "GLRenderManager.h" +#include "thread/threadutil.h" +#include "base/logging.h" + +#if 0 // def _DEBUG +#define VLOG ILOG +#else +#define VLOG(...) +#endif + +void GLCreateImage(GLRImage &img, int width, int height, GLuint format, bool color) { + +} + +void GLDeleter::Perform() { + for (auto shader : shaders) { + delete shader; + } + for (auto program : programs) { + delete program; + } + // .. +} + +GLRenderManager::GLRenderManager() { + for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { + + } +} + +GLRenderManager::~GLRenderManager() { + for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { + + } +} + +void GLRenderManager::ThreadFunc() { + setCurrentThreadName("RenderMan"); + int threadFrame = threadInitFrame_; + bool nextFrame = false; + bool firstFrame = true; + while (true) { + { + if (nextFrame) { + threadFrame++; + if (threadFrame >= MAX_INFLIGHT_FRAMES) + threadFrame = 0; + } + FrameData &frameData = frameData_[threadFrame]; + std::unique_lock 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. + break; + } + 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); + } + + VLOG("PULL: Quitting"); +} + +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 lock(frameData.push_mutex); + frameData.push_condVar.notify_all(); + } + { + std::unique_lock lock(frameData.pull_mutex); + frameData.pull_condVar.notify_all(); + } + } + + // TODO: Wait for something here! + + 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; + for (size_t i = 0; i < frameData.steps.size(); i++) { + delete frameData.steps[i]; + } + frameData.steps.clear(); + + std::unique_lock 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, 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) { + // 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 == GLRRenderPassAction::KEEP && curRenderStep_->render.depthStencil == GLRRenderPassAction::KEEP) { + // Can trivially kill the last empty render step. + assert(steps_.back() == curRenderStep_); + delete steps_.back(); + steps_.pop_back(); + curRenderStep_ = nullptr; + } + 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.color = color; + step->render.depthStencil = depth; + step->render.clearColor = clearColor; + step->render.clearDepth = clearDepth; + step->render.clearStencil = clearStencil; + step->render.numDraws = 0; + steps_.push_back(step); + + curRenderStep_ = step; + curWidth_ = fb ? fb->width : 0; // vulkan_->GetBackbufferWidth(); + curHeight_ = fb ? fb->height : 0; // vulkan_->GetBackbufferHeight(); +} + +GLuint GLRenderManager::BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit, int attachment) { + // Easy in GL. + return fb->color.texture; +} + +void GLRenderManager::CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLOffset2D dstPos, int aspectMask) { + +} + +void GLRenderManager::BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLRect2D dstRect, int aspectMask, bool filter) { + +} + +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 lock(frameData.push_mutex); + while (!frameData.readyForFence) { + VLOG("PUSH: Waiting for frame[%d].readyForFence = 1", curFrame); + frameData.push_condVar.wait(lock); + } + frameData.readyForFence = false; + } + + VLOG("PUSH: Fencing %d", curFrame); + + // vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); + // vkResetFences(device, 1, &frameData.fence); + + // Must be after the fence - this performs deletes. + VLOG("PUSH: BeginFrame %d", curFrame); + if (!run_) { + WLOG("BeginFrame while !run_!"); + } + + // vulkan_->BeginFrame(); + frameData.deleter.Perform(); + + insideFrame_ = true; +} + +void GLRenderManager::Finish() { + curRenderStep_ = nullptr; + int curFrame = GetCurFrame(); + FrameData &frameData = frameData_[curFrame]; + if (!useThread_) { + frameData.steps = std::move(steps_); + frameData.type = GLRRunType::END; + Run(curFrame); + } else { + std::unique_lock lock(frameData.pull_mutex); + VLOG("PUSH: Frame[%d].readyForRun = true", curFrame); + frameData.steps = std::move(steps_); + frameData.readyForRun = true; + frameData.type = GLRRunType::END; + frameData.pull_condVar.notify_all(); + } + + // vulkan_->EndFrame(); + frameData_[curFrame_].deleter.Take(deleter_); + + curFrame_++; + + 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 lock(frameData.push_mutex); + frameData.readyForFence = true; + frameData.push_condVar.notify_all(); + } +} + +void GLRenderManager::EndSubmitFrame(int frame) { + FrameData &frameData = frameData_[frame]; + frameData.hasBegun = false; + + Submit(frame, true); + + if (!frameData.skipSwap) { + // glSwapBuffers(); + } else { + frameData.skipSwap = false; + } +} + +void GLRenderManager::Run(int frame) { + BeginSubmitFrame(frame); + + FrameData &frameData = frameData_[frame]; + 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: + EndSubmitFrame(frame); + break; + + case GLRRunType::SYNC: + EndSyncFrame(frame); + break; + + default: + assert(false); + } + + VLOG("PULL: Finished running frame %d", frame); +} + +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(); + // vkDeviceWaitIdle(vulkan_->GetDevice()); + + // 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 lock(frameData.push_mutex); + frameData.readyForFence = true; + frameData.push_condVar.notify_all(); + } +} + +void GLRenderManager::Wipe() { + for (auto step : steps_) { + delete step; + } + steps_.clear(); +} \ No newline at end of file diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h new file mode 100644 index 0000000000..43bdaeaeb8 --- /dev/null +++ b/ext/native/thin3d/GLRenderManager.h @@ -0,0 +1,297 @@ +#pragma once + +#include +#include + +#include "gfx/gl_common.h" +#include "math/dataconv.h" +#include "Common/Log.h" +#include "GLQueueRunner.h" + +struct GLRImage { + GLuint texture; + GLuint format; +}; + +void GLCreateImage(GLRImage &img, int width, int height, GLint format, bool color); + +class GLRFramebuffer { +public: + GLRFramebuffer(int _width, int _height) { + width = _width; + height = _height; + + /* + CreateImage(vulkan_, initCmd, color, width, height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true); + CreateImage(vulkan_, initCmd, depth, width, height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false); + + VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; + VkImageView views[2]{}; + + fbci.renderPass = renderPass; + fbci.attachmentCount = 2; + fbci.pAttachments = views; + views[0] = color.imageView; + views[1] = depth.imageView; + fbci.width = width; + fbci.height = height; + fbci.layers = 1; + + vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf);*/ + } + + ~GLRFramebuffer() { + glDeleteTextures(1, &color.texture); + glDeleteRenderbuffers(1, &depth.texture); + } + + int numShadows = 1; // TODO: Support this. + + GLuint framebuf = 0; + GLRImage color{}; + GLRImage depth{}; + int width = 0; + int height = 0; +}; + +// 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; +}; + +class GLRProgram { +public: + ~GLRProgram() { + if (program) { + glDeleteProgram(program); + } + } + GLuint program = 0; +}; + +class GLRTexture { +public: + ~GLRTexture() { + if (texture) { + glDeleteTextures(1, &texture); + } + } + GLuint texture; +}; + +class GLRBuffer { +public: + ~GLRBuffer() { + if (texture) { + glDeleteTextures(1, &texture); + } + } + GLuint texture; +}; + +enum class GLRRunType { + END, + SYNC, +}; + +class GLDeleter { +public: + void Perform(); + + void Take(GLDeleter &other) { + shaders = std::move(other.shaders); + programs = std::move(other.programs); + } + + std::vector shaders; + std::vector programs; +}; + + +class GLRenderManager { +public: + GLRenderManager(); + ~GLRenderManager(); + + void ThreadFunc(); + + // 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(); + + // Creation commands. These were not needed in Vulkan since there we can do that on the main thread. + GLRTexture *CreateTexture(int w, int h) { + GLRInitStep step{ GLRInitStepType::CREATE_TEXTURE }; + step.create_texture.texture = new GLRTexture(); + step.create_texture.width = w; + step.create_texture.height = h; + initSteps_.push_back(step); + return step.create_texture.texture; + } + + GLRShader *CreateShader(const char *code) { + GLRInitStep step{ GLRInitStepType::CREATE_SHADER }; + step.create_shader.shader = new GLRShader(); + step.create_shader.code = code; + initSteps_.push_back(step); + return step.create_shader.shader; + } + + GLRProgram *CreateProgram(GLRShader *vshader, GLRShader *fshader) { + GLRInitStep step{ GLRInitStepType::CREATE_PROGRAM }; + step.create_program.program = new GLRProgram(); + step.create_program.vshader = vshader; + step.create_program.fshader = fshader; + initSteps_.push_back(step); + return step.create_program.program; + } + + void BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); + GLuint 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(GLuint 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); + + 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 SetStencil(bool enabled, uint8_t writeMask, uint8_t compareMask, uint8_t refValue) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); + GLRRenderData data{ GLRRenderCommand::STENCIL }; + data.stencil.stencilWriteMask = writeMask; + data.stencil.stencilCompareMask = compareMask; + data.stencil.stencilRef = refValue; + curRenderStep_->commands.push_back(data); + } + + void SetBlendFactor(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 Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { + _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; + 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) { + _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 = 1; + data.drawIndexed.indices = indices; + curRenderStep_->commands.push_back(data); + curRenderStep_->render.numDraws++; + } + + enum { MAX_INFLIGHT_FRAMES = 3 }; + +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); + + void StopThread(); + + int GetCurFrame() const { + return curFrame_; + } + + // 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 skipSwap = false; + GLRRunType type = GLRRunType::END; + + // GLuint fence; For future AZDO stuff? + std::vector steps; + std::vector initSteps; + + // Swapchain. + bool hasBegun = false; + uint32_t curSwapchainImage = -1; + + GLDeleter deleter; + }; + + FrameData frameData_[MAX_INFLIGHT_FRAMES]; + + // Submission time state + int curWidth_; + int curHeight_; + bool insideFrame_ = false; + GLRStep *curRenderStep_ = nullptr; + std::vector steps_; + std::vector initSteps_; + + // Execution time state + bool run_ = true; + // Thread is managed elsewhere, and should call ThreadFunc. + std::mutex mutex_; + int threadInitFrame_ = 0; + GLQueueRunner queueRunner_; + + GLDeleter deleter_; + + bool useThread_ = false; + + int curFrame_ = 0; +}; + diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 95f9f6532a..c2accb0479 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -14,6 +14,8 @@ #include "gfx/GLStateCache.h" #include "gfx_es2/gpu_features.h" +#include "thin3d/GLRenderManager.h" + #ifdef IOS extern void bindDefaultFBO(); #endif @@ -546,6 +548,8 @@ private: void fbo_unbind(); void ApplySamplers(); + GLRenderManager renderManager_; + std::vector boundSamplers_; OpenGLTexture *boundTextures_[8]{}; int maxTextures_ = 0; From dc72a8696f4ccbd9a09518d88fb466153308837d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 18 Nov 2017 16:50:49 +0100 Subject: [PATCH 02/96] More work on gl-render-manager --- ext/native/thin3d/GLQueueRunner.cpp | 108 +++++++++++++++++- ext/native/thin3d/GLQueueRunner.h | 64 +++++++---- ext/native/thin3d/GLRenderManager.h | 71 ++++++++++-- ext/native/thin3d/thin3d_gl.cpp | 163 +++++++--------------------- 4 files changed, 244 insertions(+), 162 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index ff4f90ccba..bdea224380 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -1,5 +1,7 @@ #include "GLQueueRunner.h" #include "GLRenderManager.h" +#include "base/logging.h" +#include "gfx/gl_common.h" #include "gfx_es2/gpu_features.h" #include "math/dataconv.h" @@ -12,7 +14,90 @@ void GLQueueRunner::DestroyDeviceObjects() { } void GLQueueRunner::RunInitSteps(const std::vector &steps) { + for (int i = 0; i < steps.size(); i++) { + const GLRInitStep &step = steps[i]; + switch (step.stepType) { + case GLRInitStepType::CREATE_TEXTURE: + break; + case GLRInitStepType::CREATE_PROGRAM: + { + GLRProgram *program = step.create_program.program; + program->program = glCreateProgram(); + for (int i = 0; i < step.create_program.num_shaders; i++) { + glAttachShader(program->program, step.create_program.shaders[i]->shader); + } + for (auto iter : program->semantics_) { + glBindAttribLocation(program->program, iter.location, iter.attrib); + } + glLinkProgram(program->program); + + GLint linkStatus = GL_FALSE; + glGetProgramiv(program->program, GL_LINK_STATUS, &linkStatus); + if (linkStatus != GL_TRUE) { + GLint bufLength = 0; + glGetProgramiv(program->program, GL_INFO_LOG_LENGTH, &bufLength); + if (bufLength) { + char* buf = new char[bufLength]; + glGetProgramInfoLog(program->program, bufLength, NULL, buf); + ELOG("Could not link program:\n %s", buf); + // We've thrown out the source at this point. Might want to do something about that. +#ifdef _WIN32 + OutputDebugStringUTF8(buf); +#endif + delete[] buf; + } else { + ELOG("Could not link program with %d shaders for unknown reason:", step.create_program.num_shaders); + } + break; + } + + // Auto-initialize samplers. + glUseProgram(program->program); + for (int i = 0; i < 4; i++) { + char temp[256]; + sprintf(temp, "Sampler%i", i); + int samplerLoc = glGetUniformLocation(program->program, temp); + if (samplerLoc != -1) { + glUniform1i(samplerLoc, i); + } + } + + // Here we could (using glGetAttribLocation) save a bitmask about which pieces of vertex data are used in the shader + // and then AND it with the vertex format bitmask later... + } + break; + case GLRInitStepType::CREATE_SHADER: + { + GLuint shader = glCreateShader(step.create_shader.stage); + step.create_shader.shader->shader = shader; + // language_ = language; + const char *code = step.create_shader.code; + glShaderSource(shader, 1, &code, nullptr); + delete[] code; + 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 = 0; + glGetShaderInfoLog(shader, MAX_INFO_LOG_SIZE, &len, infoLog); + infoLog[len] = '\0'; + glDeleteShader(shader); + shader = 0; + ILOG("%s Shader compile error:\n%s", step.create_shader.stage == GL_FRAGMENT_SHADER ? "Fragment" : "Vertex", infoLog); + } + break; + } + case GLRInitStepType::CREATE_BUFFER: + glGenBuffers(1, &step.create_buffer.buffer->buffer); + break; + + case GLRInitStepType::TEXTURE_SUBDATA: + break; + } + } } void GLQueueRunner::RunSteps(const std::vector &steps) { @@ -54,6 +139,8 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { return; } + glEnable(GL_SCISSOR_TEST); + // This is supposed to bind a vulkan render pass to the command buffer. PerformBindFramebufferAsRenderTarget(step); @@ -129,9 +216,9 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glUniformMatrix4fv(c.uniformMatrix4.loc, 1, false, c.uniformMatrix4.m); break; case GLRRenderCommand::STENCIL: - glStencilFunc(c.stencil.stencilFunc, c.stencil.stencilRef, c.stencil.stencilCompareMask); - glStencilOp(c.stencil.stencilSFail, c.stencil.stencilZFail, c.stencil.stencilPass); - glStencilMask(c.stencil.stencilWriteMask); + glStencilFunc(c.stencil.func, c.stencil.ref, c.stencil.compareMask); + glStencilOp(c.stencil.sFail, c.stencil.zFail, c.stencil.pass); + glStencilMask(c.stencil.writeMask); break; case GLRRenderCommand::BINDTEXTURE: { @@ -140,7 +227,12 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glActiveTexture(target); activeTexture = target; } - glBindTexture(GL_TEXTURE_2D, c.texture.texture); + glBindTexture(GL_TEXTURE_2D, c.texture.texture->texture); + break; + } + case GLRRenderCommand::BINDPROGRAM: + { + glUseProgram(c.program.program->program); break; } case GLRRenderCommand::DRAW: @@ -208,6 +300,14 @@ void GLQueueRunner::PerformCopy(const GLRStep &step) { #endif } +void GLQueueRunner::PerformReadback(const GLRStep &pass) { + +} + +void GLQueueRunner::PerformReadbackImage(const GLRStep &pass) { + +} + void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) { } diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index a691927a26..e16ada4562 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -18,6 +18,9 @@ struct GLOffset2D { int x, y; }; +class GLRProgram; +class GLRTexture; + enum class GLRRenderCommand : uint8_t { DEPTH, STENCIL, @@ -28,7 +31,9 @@ enum class GLRRenderCommand : uint8_t { TEXTURESAMPLER, VIEWPORT, SCISSOR, + RASTER, CLEAR, + BINDPROGRAM, BINDTEXTURE, DRAW, DRAW_INDEXED, @@ -38,6 +43,19 @@ enum class GLRRenderCommand : uint8_t { 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; GLboolean write; @@ -45,14 +63,13 @@ struct GLRRenderData { } depth; struct { GLboolean enabled; - GLenum stencilOp; - GLenum stencilFunc; - uint8_t stencilWriteMask; - uint8_t stencilCompareMask; - uint8_t stencilRef; - GLenum stencilSFail; - GLenum stencilZFail; - GLenum stencilPass; + GLenum func; + uint8_t writeMask; + uint8_t compareMask; + uint8_t ref; + GLenum sFail; + GLenum zFail; + GLenum pass; } stencil; struct { GLenum mode; // primitive @@ -84,8 +101,11 @@ struct GLRRenderData { } clear; struct { int slot; - GLint texture; + GLRTexture *texture; } texture; + struct { + GLRProgram *program; + } program; struct { GLuint wrapU; GLuint wrapV; @@ -100,17 +120,10 @@ struct GLRRenderData { GLRect2D rc; } scissor; struct { - GLboolean enabled; - GLenum srcColor; - GLenum dstColor; - GLenum srcAlpha; - GLenum dstAlpha; - GLenum funcColor; - GLenum funcAlpha; - } blend; - struct { - float color[4]; - } blendColor; + GLboolean cullEnable; + GLenum frontFace; + GLenum cullFace; + } raster; }; }; @@ -130,6 +143,7 @@ enum class GLRInitStepType : uint8_t { class GLRShader; class GLRTexture; class GLRProgram; +class GLRBuffer; struct GLRInitStep { GLRInitStep(GLRInitStepType _type) : stepType(_type) {} @@ -143,13 +157,17 @@ struct GLRInitStep { } create_texture; struct { GLRShader *shader; - const char *code; + char *code; + GLuint stage; } create_shader; struct { GLRProgram *program; - GLRShader *vshader; - GLRShader *fshader; + GLRShader *shaders[3]; + int num_shaders; } create_program; + struct { + GLRBuffer *buffer; + } create_buffer; }; }; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 43bdaeaeb8..c44246c604 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -2,6 +2,7 @@ #include #include +#include #include "gfx/gl_common.h" #include "math/dataconv.h" @@ -74,7 +75,12 @@ public: glDeleteProgram(program); } } + struct Semantic { + int location; + const char *attrib; + }; GLuint program = 0; + std::vector semantics_; }; class GLRTexture { @@ -90,11 +96,11 @@ public: class GLRBuffer { public: ~GLRBuffer() { - if (texture) { - glDeleteTextures(1, &texture); + if (buffer) { + glDeleteBuffers(1, &buffer); } } - GLuint texture; + GLuint buffer; }; enum class GLRRunType { @@ -142,23 +148,35 @@ public: return step.create_texture.texture; } - GLRShader *CreateShader(const char *code) { + GLRShader *CreateShader(GLuint stage, std::string &code) { GLRInitStep step{ GLRInitStepType::CREATE_SHADER }; step.create_shader.shader = new GLRShader(); - step.create_shader.code = code; + 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; } - GLRProgram *CreateProgram(GLRShader *vshader, GLRShader *fshader) { + GLRProgram *CreateProgram(std::vector shaders, std::vector semantics) { GLRInitStep step{ GLRInitStepType::CREATE_PROGRAM }; + assert(shaders.size() <= ARRAY_SIZE(step.create_program.shaders)); step.create_program.program = new GLRProgram(); - step.create_program.vshader = vshader; - step.create_program.fshader = fshader; + step.create_program.program->semantics_ = semantics; + for (int i = 0; i < shaders.size(); i++) { + step.create_program.shaders[i] = shaders[i]; + } initSteps_.push_back(step); return step.create_program.program; } + void DeleteShader(GLRShader *shader) { + deleter_.shaders.push_back(shader); + } + void DeleteProgram(GLRProgram *program) { + deleter_.programs.push_back(program); + } + void BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); GLuint 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); @@ -167,6 +185,22 @@ public: 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); + void BindProgram(GLRProgram *program) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); + GLRRenderData data{ GLRRenderCommand::BINDPROGRAM }; + data.program.program = program; + 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 }; @@ -181,12 +215,16 @@ public: curRenderStep_->commands.push_back(data); } - void SetStencil(bool enabled, uint8_t writeMask, uint8_t compareMask, uint8_t refValue) { + void SetStencil(bool enabled, GLenum func, GLenum sFail, GLenum zFail, GLenum pass, uint8_t writeMask, uint8_t compareMask, uint8_t refValue) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData data{ GLRRenderCommand::STENCIL }; - data.stencil.stencilWriteMask = writeMask; - data.stencil.stencilCompareMask = compareMask; - data.stencil.stencilRef = refValue; + data.stencil.func = func; + data.stencil.sFail = sFail; + data.stencil.zFail = zFail; + data.stencil.pass = pass; + data.stencil.writeMask = writeMask; + data.stencil.compareMask = compareMask; + data.stencil.ref = refValue; curRenderStep_->commands.push_back(data); } @@ -197,6 +235,15 @@ public: curRenderStep_->commands.push_back(data); } + void SetRaster(GLboolean cullEnable, GLenum frontFace, GLenum cullFace) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); + GLRRenderData data{ GLRRenderCommand::RASTER }; + data.raster.cullEnable = cullEnable; + data.raster.frontFace = frontFace; + data.raster.cullFace = cullFace; + curRenderStep_->commands.push_back(data); + } + void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData data{ GLRRenderCommand::CLEAR }; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index c2accb0479..6d1635723e 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -172,12 +172,10 @@ public: bool enabled; GLuint eqCol, eqAlpha; GLuint srcCol, srcAlpha, dstCol, dstAlpha; - bool logicEnabled; - GLuint logicOp; int colorMask; // uint32_t fixedColor; - void Apply() { + void Apply(GLRenderManager *render) { if (enabled) { glEnable(GL_BLEND); glBlendEquationSeparate(eqCol, eqAlpha); @@ -186,15 +184,6 @@ public: glDisable(GL_BLEND); } glColorMask(colorMask & 1, (colorMask >> 1) & 1, (colorMask >> 2) & 1, (colorMask >> 3) & 1); - -#if !defined(USING_GLES2) - if (logicEnabled) { - glEnable(GL_COLOR_LOGIC_OP); - glLogicOp(logicOp); - } else { - glDisable(GL_COLOR_LOGIC_OP); - } -#endif } }; @@ -223,36 +212,16 @@ public: uint8_t stencilCompareMask; uint8_t stencilWriteMask; - void Apply() { - if (depthTestEnabled) { - glEnable(GL_DEPTH_TEST); - glDepthFunc(depthComp); - glDepthMask(depthWriteEnabled); - } else { - glDisable(GL_DEPTH_TEST); - } - if (stencilEnabled) { - glEnable(GL_STENCIL_TEST); - glStencilOpSeparate(GL_FRONT_AND_BACK, stencilFail, stencilZFail, stencilPass); - glStencilFuncSeparate(GL_FRONT_AND_BACK, stencilCompareOp, stencilReference, stencilCompareMask); - glStencilMaskSeparate(GL_FRONT_AND_BACK, stencilWriteMask); - } else { - glDisable(GL_STENCIL_TEST); - } + void Apply(GLRenderManager *render) { + render->SetDepth(depthTestEnabled, depthWriteEnabled, depthComp); + render->SetStencil(stencilEnabled, stencilCompareOp, stencilFail, stencilZFail, stencilPass, stencilWriteMask, stencilCompareMask, stencilReference); } }; class OpenGLRasterState : public RasterState { public: - void Apply() { - glEnable(GL_SCISSOR_TEST); - if (!cullEnable) { - glDisable(GL_CULL_FACE); - return; - } - glEnable(GL_CULL_FACE); - glFrontFace(frontFace); - glCullFace(cullMode); + void Apply(GLRenderManager *render) { + render->SetRaster(cullEnable, frontFace, cullMode); } GLboolean cullEnable; @@ -277,17 +246,18 @@ GLuint ShaderStageToOpenGL(ShaderStage stage) { class OpenGLShaderModule : public ShaderModule { public: - OpenGLShaderModule(ShaderStage stage) : stage_(stage) { + OpenGLShaderModule(GLRenderManager *render, ShaderStage stage) : render_(render), stage_(stage) { + ILOG("Shader module created (%p)", this); glstage_ = ShaderStageToOpenGL(stage); } ~OpenGLShaderModule() { if (shader_) - glDeleteShader(shader_); + render_->DeleteShader(shader_); } - bool Compile(ShaderLanguage language, const uint8_t *data, size_t dataSize); - GLuint GetShader() const { + bool Compile(GLRenderManager *render, ShaderLanguage language, const uint8_t *data, size_t dataSize); + GLRShader *GetShader() const { return shader_; } const std::string &GetSource() const { return source_; } @@ -300,19 +270,17 @@ public: } private: + GLRenderManager *render_; ShaderStage stage_; ShaderLanguage language_; - GLuint shader_ = 0; + GLRShader *shader_ = nullptr; GLuint glstage_ = 0; bool ok_ = false; std::string source_; // So we can recompile in case of context loss. }; -bool OpenGLShaderModule::Compile(ShaderLanguage language, const uint8_t *data, size_t dataSize) { +bool OpenGLShaderModule::Compile(GLRenderManager *render, ShaderLanguage language, const uint8_t *data, size_t dataSize) { source_ = std::string((const char *)data); - shader_ = glCreateShader(glstage_); - language_ = language; - std::string temp; // Add the prelude on automatically. if (glstage_ == GL_FRAGMENT_SHADER || glstage_ == GL_VERTEX_SHADER) { @@ -320,23 +288,8 @@ bool OpenGLShaderModule::Compile(ShaderLanguage language, const uint8_t *data, s source_ = temp.c_str(); } - const char *code = source_.c_str(); - glShaderSource(shader_, 1, &code, nullptr); - 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 = 0; - glGetShaderInfoLog(shader_, MAX_INFO_LOG_SIZE, &len, infoLog); - infoLog[len] = '\0'; - glDeleteShader(shader_); - shader_ = 0; - ILOG("%s Shader compile error:\n%s", glstage_ == GL_FRAGMENT_SHADER ? "Fragment" : "Vertex", infoLog); - } - ok_ = success != 0; - return ok_; + shader_ = render->CreateShader(glstage_, source_); + return true; } class OpenGLInputLayout : public InputLayout { @@ -365,14 +318,13 @@ struct UniformInfo { class OpenGLPipeline : public Pipeline { public: - OpenGLPipeline() { - program_ = 0; + OpenGLPipeline(GLRenderManager *render) : render_(render) { } ~OpenGLPipeline() { for (auto &iter : shaders) { iter->Release(); } - glDeleteProgram(program_); + render_->DeleteProgram(program_); if (depthStencil) depthStencil->Release(); if (blend) blend->Release(); if (raster) raster->Release(); @@ -397,8 +349,9 @@ public: // TODO: Optimize by getting the locations first and putting in a custom struct UniformBufferDesc dynamicUniforms; - GLuint program_; + GLRProgram *program_ = nullptr; private: + GLRenderManager *render_; std::map uniformCache_; }; @@ -976,10 +929,6 @@ BlendState *OpenGLContext::CreateBlendState(const BlendStateDesc &desc) { bs->eqAlpha = blendEqToGL[(int)desc.eqAlpha]; bs->srcAlpha = blendFactorToGL[(int)desc.srcAlpha]; bs->dstAlpha = blendFactorToGL[(int)desc.dstAlpha]; -#ifndef USING_GLES2 - bs->logicEnabled = desc.logicEnabled; - bs->logicOp = logicOpToGL[(int)desc.logicOp]; -#endif bs->colorMask = desc.colorMask; return bs; } @@ -1077,7 +1026,7 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) { ELOG("Pipeline requires at least one shader"); return NULL; } - OpenGLPipeline *pipeline = new OpenGLPipeline(); + OpenGLPipeline *pipeline = new OpenGLPipeline(&renderManager_); for (auto iter : desc.shaders) { iter->AddRef(); pipeline->shaders.push_back(static_cast(iter)); @@ -1149,8 +1098,8 @@ void OpenGLContext::ApplySamplers() { } ShaderModule *OpenGLContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize) { - OpenGLShaderModule *shader = new OpenGLShaderModule(stage); - if (shader->Compile(language, data, dataSize)) { + OpenGLShaderModule *shader = new OpenGLShaderModule(&renderManager_, stage); + if (shader->Compile(&renderManager_, language, data, dataSize)) { return shader; } else { shader->Release(); @@ -1159,53 +1108,20 @@ ShaderModule *OpenGLContext::CreateShaderModule(ShaderStage stage, ShaderLanguag } bool OpenGLPipeline::LinkShaders() { - program_ = glCreateProgram(); + std::vector linkShaders; for (auto iter : shaders) { - glAttachShader(program_, iter->GetShader()); + linkShaders.push_back(iter->GetShader()); } - + std::vector semantics; + semantics.push_back({ SEM_POSITION, "Position" }); // Bind all the common vertex data points. Mismatching ones will be ignored. - glBindAttribLocation(program_, SEM_POSITION, "Position"); - glBindAttribLocation(program_, SEM_COLOR0, "Color0"); - glBindAttribLocation(program_, SEM_TEXCOORD0, "TexCoord0"); - glBindAttribLocation(program_, SEM_NORMAL, "Normal"); - glBindAttribLocation(program_, SEM_TANGENT, "Tangent"); - glBindAttribLocation(program_, SEM_BINORMAL, "Binormal"); - glLinkProgram(program_); - - 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); - ELOG("Could not link program:\n %s", buf); - // We've thrown out the source at this point. Might want to do something about that. -#ifdef _WIN32 - OutputDebugStringUTF8(buf); -#endif - delete[] buf; - } else { - ELOG("Could not link program with %d shaders for unknown reason:", (int)shaders.size()); - } - return false; - } - - // Auto-initialize samplers. - glUseProgram(program_); - for (int i = 0; i < 4; i++) { - char temp[256]; - sprintf(temp, "Sampler%i", i); - int samplerLoc = GetUniformLoc(temp); - if (samplerLoc != -1) { - glUniform1i(samplerLoc, i); - } - } - - // Here we could (using glGetAttribLocation) save a bitmask about which pieces of vertex data are used in the shader - // and then AND it with the vertex format bitmask later... + semantics.push_back({ SEM_POSITION, "Position" }); + semantics.push_back({ SEM_COLOR0, "Color0" }); + semantics.push_back({ SEM_TEXCOORD0, "TexCoord0" }); + semantics.push_back({ SEM_NORMAL, "Normal" }); + semantics.push_back({ SEM_TANGENT, "Tangent" }); + semantics.push_back({ SEM_BINORMAL, "Binormal" }); + program_ = render_->CreateProgram(linkShaders, semantics); return true; } @@ -1215,7 +1131,8 @@ int OpenGLPipeline::GetUniformLoc(const char *name) { if (iter != uniformCache_.end()) { loc = iter->second.loc_; } else { - loc = glGetUniformLocation(program_, name); + Crash(); + // loc = glGetUniformLocation(program_, name); UniformInfo info; info.loc_ = loc; uniformCache_[name] = info; @@ -1225,10 +1142,10 @@ int OpenGLPipeline::GetUniformLoc(const char *name) { void OpenGLContext::BindPipeline(Pipeline *pipeline) { curPipeline_ = (OpenGLPipeline *)pipeline; - curPipeline_->blend->Apply(); - curPipeline_->depthStencil->Apply(); - curPipeline_->raster->Apply(); - glUseProgram(curPipeline_->program_); + curPipeline_->blend->Apply(&renderManager_); + curPipeline_->depthStencil->Apply(&renderManager_); + curPipeline_->raster->Apply(&renderManager_); + renderManager_.BindProgram(curPipeline_->program_); } void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) { From 9babdb712c5f9a1e34a5bbffdc6c0e5511d66915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 18 Nov 2017 20:17:17 +0100 Subject: [PATCH 03/96] More gl-render-manager --- ext/native/thin3d/GLQueueRunner.cpp | 86 +++++++++--- ext/native/thin3d/GLQueueRunner.h | 32 ++++- ext/native/thin3d/GLRenderManager.h | 127 ++++++++++++++++- ext/native/thin3d/thin3d.h | 5 +- ext/native/thin3d/thin3d_gl.cpp | 203 +++++++++------------------- 5 files changed, 290 insertions(+), 163 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index bdea224380..3d2167377f 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -2,6 +2,7 @@ #include "GLRenderManager.h" #include "base/logging.h" #include "gfx/gl_common.h" +#include "gfx/gl_debug_log.h" #include "gfx_es2/gpu_features.h" #include "math/dataconv.h" @@ -19,6 +20,21 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { switch (step.stepType) { case GLRInitStepType::CREATE_TEXTURE: break; + case GLRInitStepType::CREATE_BUFFER: + { + GLRBuffer *buffer = step.create_buffer.buffer; + glGenBuffers(1, &buffer->buffer); + glBindBuffer(buffer->target_, buffer->buffer); + glBufferData(buffer->target_, step.create_buffer.size, nullptr, step.create_buffer.usage); + break; + } + case GLRInitStepType::BUFFER_SUBDATA: + { + GLRBuffer *buffer = step.buffer_subdata.buffer; + glBufferSubData(buffer->buffer, step.buffer_subdata.offset, step.buffer_subdata.size, step.buffer_subdata.data); + delete[] step.buffer_subdata.data; + break; + } case GLRInitStepType::CREATE_PROGRAM: { GLRProgram *program = step.create_program.program; @@ -90,12 +106,18 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } break; } - case GLRInitStepType::CREATE_BUFFER: - glGenBuffers(1, &step.create_buffer.buffer->buffer); - break; case GLRInitStepType::TEXTURE_SUBDATA: break; + case GLRInitStepType::TEXTURE_IMAGE: + { + GLRTexture *tex = step.texture_image.texture; + CHECK_GL_ERROR_IF_DEBUG(); + glTexImage2D(tex->target, step.texture_image.level, step.texture_image.internalFormat, step.texture_image.width, step.texture_image.height, 0, step.texture_image.format, step.texture_image.type, step.texture_image.data); + delete[] step.texture_image.data; + CHECK_GL_ERROR_IF_DEBUG(); + break; + } } } } @@ -148,7 +170,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { int curHeight = step.render.framebuffer ? step.render.framebuffer->height : 0; // vulkan_->GetBackbufferHeight(); GLRFramebuffer *fb = step.render.framebuffer; - + GLRProgram *curProgram = nullptr; GLint activeTexture = GL_TEXTURE0; auto &commands = step.commands; @@ -197,24 +219,40 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glScissor(c.scissor.rc.x, c.scissor.rc.y, c.scissor.rc.w, c.scissor.rc.h); break; case GLRRenderCommand::UNIFORM4F: - switch (c.uniform4.count) { - case 1: - glUniform1f(c.uniform4.loc, c.uniform4.v[0]); - break; - case 2: - glUniform2fv(c.uniform4.loc, 1, c.uniform4.v); - break; - case 3: - glUniform3fv(c.uniform4.loc, 1, c.uniform4.v); - break; - case 4: - glUniform4fv(c.uniform4.loc, 1, c.uniform4.v); - break; + { + int loc = c.uniform4.loc; + if (c.uniform4.name) { + loc = curProgram->GetUniformLoc(c.uniform4.name); + } + if (loc >= 0) { + switch (c.uniform4.count) { + case 1: + glUniform1f(loc, c.uniform4.v[0]); + break; + case 2: + glUniform2fv(loc, 1, c.uniform4.v); + break; + case 3: + glUniform3fv(loc, 1, c.uniform4.v); + break; + case 4: + glUniform4fv(loc, 1, c.uniform4.v); + break; + } } break; + } case GLRRenderCommand::UNIFORMMATRIX: - glUniformMatrix4fv(c.uniformMatrix4.loc, 1, false, c.uniformMatrix4.m); + { + int loc = c.uniform4.loc; + if (c.uniform4.name) { + loc = curProgram->GetUniformLoc(c.uniform4.name); + } + if (loc >= 0) { + glUniformMatrix4fv(loc, 1, false, c.uniformMatrix4.m); + } break; + } case GLRRenderCommand::STENCIL: glStencilFunc(c.stencil.func, c.stencil.ref, c.stencil.compareMask); glStencilOp(c.stencil.sFail, c.stencil.zFail, c.stencil.pass); @@ -227,14 +265,18 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glActiveTexture(target); activeTexture = target; } - glBindTexture(GL_TEXTURE_2D, c.texture.texture->texture); + glBindTexture(c.texture.texture->target, c.texture.texture->texture); break; } case GLRRenderCommand::BINDPROGRAM: { glUseProgram(c.program.program->program); + curProgram = c.program.program; break; } + case GLRRenderCommand::GENMIPS: + glGenerateMipmap(GL_TEXTURE_2D); + break; case GLRRenderCommand::DRAW: glDrawArrays(c.draw.mode, c.draw.first, c.draw.count); break; @@ -243,6 +285,12 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glDrawElements(c.drawIndexed.mode, c.drawIndexed.count, c.drawIndexed.indexType, c.drawIndexed.indices); } break; + case GLRRenderCommand::TEXTURESAMPLER: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, c.textureSampler.wrapS); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c.textureSampler.wrapT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, c.textureSampler.magFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c.textureSampler.minFilter); + break; } } if (activeTexture != GL_TEXTURE0) diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index e16ada4562..e953e840a7 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -35,6 +35,7 @@ enum class GLRRenderCommand : uint8_t { CLEAR, BINDPROGRAM, BINDTEXTURE, + GENMIPS, DRAW, DRAW_INDEXED, PUSH_CONSTANTS, @@ -85,11 +86,13 @@ struct GLRRenderData { void *indices; } drawIndexed; struct { + const char *name; // if null, use loc GLint loc; GLint count; float v[4]; } uniform4; struct { + const char *name; // if null, use loc GLint loc; float m[16]; } uniformMatrix4; @@ -107,11 +110,10 @@ struct GLRRenderData { GLRProgram *program; } program; struct { - GLuint wrapU; - GLuint wrapV; - bool maxFilter; - bool minFilter; - bool mipFilter; + GLenum wrapS; + GLenum wrapT; + GLenum magFilter; + GLenum minFilter; // also includes mip. GL... } textureSampler; struct { GLRViewport vp; @@ -136,6 +138,7 @@ enum class GLRInitStepType : uint8_t { CREATE_PROGRAM, CREATE_BUFFER, + TEXTURE_IMAGE, TEXTURE_SUBDATA, BUFFER_SUBDATA, }; @@ -151,6 +154,7 @@ struct GLRInitStep { union { struct { GLRTexture *texture; + GLenum target; int width; int height; // ... @@ -167,7 +171,25 @@ struct GLRInitStep { } create_program; struct { GLRBuffer *buffer; + int size; + GLuint usage; } create_buffer; + struct { + GLRBuffer *buffer; + int offset; + int size; + uint8_t *data; // owned, delete[]-d + } buffer_subdata; + struct { + GLRTexture *texture; + GLenum internalFormat; + GLenum format; + GLenum type; + int level; + int width; + int height; + uint8_t *data; // owned, delete[]-d + } texture_image; }; }; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index c44246c604..a48103b539 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include @@ -81,6 +83,26 @@ public: }; GLuint program = 0; std::vector semantics_; + + struct UniformInfo { + int loc_; + }; + + // Must ONLY be called from GLQueueRunner! + int GetUniformLoc(const char *name) { + auto iter = uniformCache_.find(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::map uniformCache_; }; class GLRTexture { @@ -91,16 +113,20 @@ public: } } GLuint texture; + GLenum target; }; class GLRBuffer { public: + GLRBuffer(GLuint target) : target_(target) {} ~GLRBuffer() { if (buffer) { glDeleteBuffers(1, &buffer); } } GLuint buffer; + GLuint target_; +private: }; enum class GLRRunType { @@ -119,6 +145,8 @@ public: std::vector shaders; std::vector programs; + std::vector buffers; + std::vector textures; }; @@ -139,15 +167,23 @@ public: void Wipe(); // Creation commands. These were not needed in Vulkan since there we can do that on the main thread. - GLRTexture *CreateTexture(int w, int h) { + GLRTexture *CreateTexture(GLenum target, int w, int h) { GLRInitStep step{ GLRInitStepType::CREATE_TEXTURE }; step.create_texture.texture = new GLRTexture(); + step.create_texture.texture->target = target; step.create_texture.width = w; step.create_texture.height = h; initSteps_.push_back(step); return step.create_texture.texture; } + GLRBuffer *CreateBuffer(GLuint target, int size, GLuint usage) { + GLRInitStep step{ GLRInitStepType::CREATE_BUFFER }; + step.create_buffer.buffer = new GLRBuffer(target); + initSteps_.push_back(step); + return step.create_buffer.buffer; + } + GLRShader *CreateShader(GLuint stage, std::string &code) { GLRInitStep step{ GLRInitStepType::CREATE_SHADER }; step.create_shader.shader = new GLRShader(); @@ -176,6 +212,12 @@ public: void DeleteProgram(GLRProgram *program) { deleter_.programs.push_back(program); } + void DeleteBuffer(GLRBuffer *buffer) { + deleter_.buffers.push_back(buffer); + } + void DeleteTexture(GLRTexture *texture) { + deleter_.textures.push_back(texture); + } void BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); GLuint BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit, int attachment); @@ -185,6 +227,38 @@ public: 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. + void BufferSubdata(GLRBuffer *buffer, int offset, int size, uint8_t *data) { + // 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 }; + step.buffer_subdata.buffer = buffer; + step.buffer_subdata.offset = offset; + step.buffer_subdata.size = size; + step.buffer_subdata.data = data; + initSteps_.push_back(step); + } + + void TextureImage(GLRTexture *texture, int level, int width, int height, GLenum internalFormat, GLenum format, GLenum type, uint8_t *data) { + GLRInitStep step{ GLRInitStepType::TEXTURE_IMAGE }; + 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; + initSteps_.push_back(step); + } + + void BindTexture(int slot, GLRTexture *tex) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); + GLRRenderData data{ GLRRenderCommand::BINDTEXTURE }; + 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 }; @@ -192,6 +266,12 @@ public: curRenderStep_->commands.push_back(data); } + void GenerateMipmap() { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); + GLRRenderData data{ GLRRenderCommand::GENMIPS }; + 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 }; @@ -215,6 +295,40 @@ public: curRenderStep_->commands.push_back(data); } + void SetUniformF(int 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 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(int loc, const float *udata) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); + GLRRenderData data{ GLRRenderCommand::UNIFORM4F }; + 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::UNIFORM4F }; + data.uniformMatrix4.name = name; + memcpy(data.uniformMatrix4.m, udata, sizeof(float) * 16); + curRenderStep_->commands.push_back(data); + } + void SetStencil(bool enabled, GLenum func, GLenum sFail, GLenum zFail, GLenum pass, uint8_t writeMask, uint8_t compareMask, uint8_t refValue) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData data{ GLRRenderCommand::STENCIL }; @@ -243,6 +357,17 @@ public: data.raster.cullFace = cullFace; 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) { + _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; + curRenderStep_->commands.push_back(data); + } void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index b401a6268f..6bbc0d50e8 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -587,7 +587,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; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 6d1635723e..b247ca0b96 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -312,10 +312,6 @@ public: intptr_t lastBase_; }; -struct UniformInfo { - int loc_; -}; - class OpenGLPipeline : public Pipeline { public: OpenGLPipeline(GLRenderManager *render) : render_(render) { @@ -352,7 +348,6 @@ public: GLRProgram *program_ = nullptr; private: GLRenderManager *render_; - std::map uniformCache_; }; class OpenGLFramebuffer; @@ -398,8 +393,6 @@ public: // color must be 0, for now. void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override; - uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) override; - void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override; void BindSamplerStates(int start, int count, SamplerState **states) override { @@ -422,17 +415,12 @@ public: } void SetViewports(int count, Viewport *viewports) override { - // TODO: Use glViewportArrayv. - glViewport((GLint)viewports[0].TopLeftX, (GLint)viewports[0].TopLeftY, (GLsizei)viewports[0].Width, (GLsizei)viewports[0].Height); -#if defined(USING_GLES2) - glDepthRangef(viewports[0].MinDepth, viewports[0].MaxDepth); -#else - glDepthRange(viewports[0].MinDepth, viewports[0].MaxDepth); -#endif + // Same structure, different name. + renderManager_.SetViewport((GLRViewport &)*viewports); } void SetBlendFactor(float color[4]) override { - glBlendColor(color[0], color[1], color[2], color[3]); + renderManager_.SetBlendFactor(color); } void BindTextures(int start, int count, Texture **textures) override; @@ -585,7 +573,7 @@ inline bool isPowerOf2(int n) { class OpenGLTexture : public Texture { public: - OpenGLTexture(const TextureDesc &desc); + OpenGLTexture(GLRenderManager *render, const TextureDesc &desc); ~OpenGLTexture(); bool HasMips() const { @@ -595,8 +583,8 @@ public: return canWrap_; } TextureType GetType() const { return type_; } - void Bind() { - glBindTexture(target_, tex_); + void Bind(int stage) { + render_->BindTexture(stage, tex_); } void AutoGenMipmaps(); @@ -604,8 +592,8 @@ public: private: void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data); - GLuint tex_ = 0; - GLuint target_ = 0; + GLRenderManager *render_; + GLRTexture *tex_; DataFormat format_; TextureType type_; @@ -614,7 +602,7 @@ private: bool canWrap_; }; -OpenGLTexture::OpenGLTexture(const TextureDesc &desc) { +OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : render_(render) { generatedMips_ = false; canWrap_ = true; width_ = desc.width; @@ -622,16 +610,16 @@ OpenGLTexture::OpenGLTexture(const TextureDesc &desc) { depth_ = desc.depth; format_ = desc.format; type_ = desc.type; - target_ = TypeToTarget(desc.type); + GLenum target = TypeToTarget(desc.type); + tex_ = render->CreateTexture(target, width_, height_); + + render->BindTexture(0, tex_); + canWrap_ = isPowerOf2(width_) && isPowerOf2(height_); mipLevels_ = desc.mipLevels; if (!desc.initData.size()) return; - glActiveTexture(GL_TEXTURE0 + 0); - glGenTextures(1, &tex_); - glBindTexture(target_, tex_); - int level = 0; for (auto data : desc.initData) { SetImageData(0, 0, 0, width_, height_, depth_, level, 0, data); @@ -641,6 +629,8 @@ OpenGLTexture::OpenGLTexture(const TextureDesc &desc) { } mipLevels_ = desc.generateMips ? desc.mipLevels : level; + render->SetTextureSampler(GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_LINEAR, mipLevels_ > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); + /* #ifdef USING_GLES2 if (gl_extensions.GLES3) { glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, mipLevels_ - 1); @@ -648,8 +638,7 @@ OpenGLTexture::OpenGLTexture(const TextureDesc &desc) { #else glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, mipLevels_ - 1); #endif - glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, mipLevels_ > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); - glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +*/ if ((int)desc.initData.size() < desc.mipLevels && desc.generateMips) { ILOG("Generating mipmaps"); @@ -657,12 +646,12 @@ OpenGLTexture::OpenGLTexture(const TextureDesc &desc) { } // Unbind. - glBindTexture(target_, 0); + render->BindTexture(0, nullptr); } OpenGLTexture::~OpenGLTexture() { if (tex_) { - glDeleteTextures(1, &tex_); + render_->DeleteTexture(tex_); tex_ = 0; generatedMips_ = false; } @@ -670,8 +659,8 @@ OpenGLTexture::~OpenGLTexture() { void OpenGLTexture::AutoGenMipmaps() { if (!generatedMips_) { - glBindTexture(target_, tex_); - glGenerateMipmap(target_); + render_->BindTexture(0, tex_); + render_->GenerateMipmap(); generatedMips_ = true; } } @@ -681,6 +670,8 @@ public: OpenGLFramebuffer() {} ~OpenGLFramebuffer(); + GLRFramebuffer *framebuffer; + GLuint handle = 0; GLuint color_texture = 0; GLuint z_stencil_buffer = 0; // Either this is set, or the two below. @@ -792,16 +783,12 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int return; } - CHECK_GL_ERROR_IF_DEBUG(); - switch (target_) { - case GL_TEXTURE_2D: - glTexImage2D(GL_TEXTURE_2D, level, internalFormat, width_, height_, 0, format, type, data); - break; - default: - ELOG("Thin3D GL: Targets other than GL_TEXTURE_2D not yet supported"); - break; + // Make a copy of data with stride eliminated. + uint8_t *texData = new uint8_t[width * height * alignment]; + for (int y = 0; y < height; y++) { + memcpy(texData + y * width * alignment, data + y * stride * alignment, width * alignment); } - CHECK_GL_ERROR_IF_DEBUG(); + render_->TextureImage(tex_, level, width, height, internalFormat, format, type, texData); } @@ -879,7 +866,7 @@ bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBit Texture *OpenGLContext::CreateTexture(const TextureDesc &desc) { - return new OpenGLTexture(desc); + return new OpenGLTexture(&renderManager_, desc); } OpenGLInputLayout::~OpenGLInputLayout() { @@ -978,28 +965,29 @@ RasterState *OpenGLContext::CreateRasterState(const RasterStateDesc &desc) { class OpenGLBuffer : public Buffer { public: - OpenGLBuffer(size_t size, uint32_t flags) { - glGenBuffers(1, &buffer_); + OpenGLBuffer(GLRenderManager *render, size_t size, uint32_t flags) : render_(render) { target_ = (flags & BufferUsageFlag::INDEXDATA) ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER; usage_ = 0; if (flags & BufferUsageFlag::DYNAMIC) usage_ = GL_STREAM_DRAW; else usage_ = GL_STATIC_DRAW; + buffer_ = render->CreateBuffer(target_, size, usage_); totalSize_ = size; - glBindBuffer(target_, buffer_); - glBufferData(target_, size, NULL, usage_); } ~OpenGLBuffer() override { - glDeleteBuffers(1, &buffer_); + render_->DeleteBuffer(buffer_); } void Bind(int offset) { + Crash(); + // render_->BindBuffer(buffer_); // TODO: Can't support offset using ES 2.0 - glBindBuffer(target_, buffer_); + // glBindBuffer(target_, buffer_); } - GLuint buffer_; + GLRenderManager *render_; + GLRBuffer *buffer_; GLuint target_; GLuint usage_; @@ -1007,7 +995,7 @@ public: }; Buffer *OpenGLContext::CreateBuffer(size_t size, uint32_t usageFlags) { - return new OpenGLBuffer(size, usageFlags); + return new OpenGLBuffer(&renderManager_, size, usageFlags); } void OpenGLContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) { @@ -1017,14 +1005,17 @@ void OpenGLContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t off if (size + offset > buf->totalSize_) { Crash(); } + + uint8_t *dataCopy = new uint8_t[size]; + memcpy(dataCopy, data, size); // if (flags & UPDATE_DISCARD) we could try to orphan the buffer using glBufferData. - glBufferSubData(buf->target_, offset, size, data); + renderManager_.BufferSubdata(buf->buffer_, offset, size, dataCopy); } Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) { if (!desc.shaders.size()) { ELOG("Pipeline requires at least one shader"); - return NULL; + return nullptr; } OpenGLPipeline *pipeline = new OpenGLPipeline(&renderManager_); for (auto iter : desc.shaders) { @@ -1056,16 +1047,14 @@ void OpenGLContext::BindTextures(int start, int count, Texture **textures) { maxTextures_ = std::max(maxTextures_, start + count); for (int i = start; i < start + count; i++) { OpenGLTexture *glTex = static_cast(textures[i]); - glActiveTexture(GL_TEXTURE0 + i); if (!glTex) { boundTextures_[i] = 0; - glBindTexture(GL_TEXTURE_2D, 0); + renderManager_.BindTexture(i, nullptr); continue; } - glTex->Bind(); + glTex->Bind(i); boundTextures_[i] = glTex; } - glActiveTexture(GL_TEXTURE0); } void OpenGLContext::ApplySamplers() { @@ -1075,24 +1064,18 @@ void OpenGLContext::ApplySamplers() { const OpenGLTexture *tex = boundTextures_[i]; if (!tex) continue; + GLenum wrapS; + GLenum wrapT; if (tex->CanWrap()) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, samp->wrapU); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, samp->wrapV); -#ifndef USING_GLES2 - if (tex->GetType() == TextureType::LINEAR3D) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, samp->wrapW); -#endif + wrapS = samp->wrapU; + wrapT = samp->wrapV; } else { - 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, samp->magFilt); - if (tex->HasMips()) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, samp->mipMinFilt); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, samp->minFilt); + wrapS = GL_CLAMP_TO_EDGE; + wrapT = GL_CLAMP_TO_EDGE; } + GLenum magFilt = samp->magFilt; + GLenum minFilt = tex->HasMips() ? samp->mipMinFilt : samp->minFilt; + renderManager_.SetTextureSampler(wrapS, wrapT, magFilt, minFilt); } } } @@ -1113,7 +1096,6 @@ bool OpenGLPipeline::LinkShaders() { linkShaders.push_back(iter->GetShader()); } std::vector semantics; - semantics.push_back({ SEM_POSITION, "Position" }); // Bind all the common vertex data points. Mismatching ones will be ignored. semantics.push_back({ SEM_POSITION, "Position" }); semantics.push_back({ SEM_COLOR0, "Color0" }); @@ -1125,21 +1107,6 @@ bool OpenGLPipeline::LinkShaders() { return true; } -int OpenGLPipeline::GetUniformLoc(const char *name) { - auto iter = uniformCache_.find(name); - int loc = -1; - if (iter != uniformCache_.end()) { - loc = iter->second.loc_; - } else { - Crash(); - // loc = glGetUniformLocation(program_, name); - UniformInfo info; - info.loc_ = loc; - uniformCache_[name] = info; - } - return loc; -} - void OpenGLContext::BindPipeline(Pipeline *pipeline) { curPipeline_ = (OpenGLPipeline *)pipeline; curPipeline_->blend->Apply(&renderManager_); @@ -1154,16 +1121,13 @@ void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) { } for (auto &uniform : curPipeline_->dynamicUniforms.uniforms) { - GLuint loc = curPipeline_->GetUniformLoc(uniform.name); - if (loc == -1) - Crash(); const float *data = (const float *)((uint8_t *)ub + uniform.offset); switch (uniform.type) { case UniformType::FLOAT4: - glUniform1fv(loc, 4, data); + renderManager_.SetUniformF(uniform.name, 4, data); break; case UniformType::MATRIX4X4: - glUniformMatrix4fv(loc, 1, false, data); + renderManager_.SetUniformM4x4(uniform.name, data); break; } } @@ -1612,42 +1576,17 @@ void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const Render void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int srcX, int srcY, int srcZ, Framebuffer *fbdst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) { OpenGLFramebuffer *src = (OpenGLFramebuffer *)fbsrc; OpenGLFramebuffer *dst = (OpenGLFramebuffer *)fbdst; - GLuint srcTex = 0; - GLuint dstTex = 0; - GLuint target = GL_TEXTURE_2D; - switch (channelBits) { - case FB_COLOR_BIT: - srcTex = src->color_texture; - dstTex = dst->color_texture; - break; - case FB_DEPTH_BIT: - target = GL_RENDERBUFFER; - srcTex = src->z_buffer ? src->z_buffer : src->z_stencil_buffer; - dstTex = dst->z_buffer ? dst->z_buffer : dst->z_stencil_buffer; - break; + + int aspect = 0; + if (channelBits & FB_COLOR_BIT) { + aspect |= GLR_ASPECT_COLOR; + } else if (channelBits & (FB_STENCIL_BIT | FB_DEPTH_BIT)) { + if (channelBits & FB_STENCIL_BIT) + aspect |= GLR_ASPECT_STENCIL; + if (channelBits & FB_DEPTH_BIT) + aspect |= GLR_ASPECT_DEPTH; } -#if defined(USING_GLES2) -#ifndef IOS - glCopyImageSubDataOES( - srcTex, target, srcLevel, srcX, srcY, srcZ, - dstTex, target, dstLevel, dstX, dstY, dstZ, - width, height, depth); -#endif -#else - if (gl_extensions.ARB_copy_image) { - glCopyImageSubData( - srcTex, target, srcLevel, srcX, srcY, srcZ, - dstTex, target, dstLevel, dstX, dstY, dstZ, - width, height, depth); - } else if (gl_extensions.NV_copy_image) { - // Older, pre GL 4.x NVIDIA cards. - glCopyImageSubDataNV( - srcTex, target, srcLevel, srcX, srcY, srcZ, - dstTex, target, dstLevel, dstX, dstY, dstZ, - width, height, depth); - } -#endif - CHECK_GL_ERROR_IF_DEBUG(); + renderManager_.CopyFramebuffer(src->framebuffer, GLRect2D{ srcX, srcY, width, height }, dst->framebuffer, GLOffset2D{ dstX, dstY }, aspect); } bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *fbdst, int dstX1, int dstY1, int dstX2, int dstY2, int channels, FBBlitFilter linearFilter) { @@ -1679,16 +1618,6 @@ bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, in } } -uintptr_t OpenGLContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) { - OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; - switch (channelBits) { - case FB_COLOR_BIT: return (uintptr_t)fb->color_texture; - case FB_DEPTH_BIT: return (uintptr_t)(fb->z_buffer ? fb->z_buffer : fb->z_stencil_buffer); - default: - return 0; - } -} - void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int color) { OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; if (!fb) From b8e4ae08a7bf7b808e7113050070f1aa6f223c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 18 Nov 2017 21:12:11 +0100 Subject: [PATCH 04/96] Even more gl-render-manager --- ext/native/thin3d/GLQueueRunner.cpp | 46 ++++++- ext/native/thin3d/GLQueueRunner.h | 21 +++- ext/native/thin3d/GLRenderManager.cpp | 9 +- ext/native/thin3d/GLRenderManager.h | 45 +++++++ ext/native/thin3d/thin3d_gl.cpp | 175 +++++++++++--------------- 5 files changed, 179 insertions(+), 117 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 3d2167377f..f5275fdc72 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -107,6 +107,12 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { break; } + case GLRInitStepType::CREATE_INPUT_LAYOUT: + { + GLRInputLayout *layout = step.create_input_layout.inputLayout; + // TODO + break; + } case GLRInitStepType::TEXTURE_SUBDATA: break; case GLRInitStepType::TEXTURE_IMAGE: @@ -195,17 +201,22 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } break; case GLRRenderCommand::CLEAR: - if (c.clear.clearMask & GLR_ASPECT_COLOR) { + if (c.clear.clearMask & GL_COLOR_BUFFER_BIT) { float color[4]; Uint8x4ToFloat4(color, c.clear.clearColor); glClearColor(color[0], color[1], color[2], color[3]); } - if (c.clear.clearMask & GLR_ASPECT_DEPTH) { + if (c.clear.clearMask & GL_DEPTH_BUFFER_BIT) { +#if defined(USING_GLES2) + glClearDepthf(c.clear.clearZ); +#else glClearDepth(c.clear.clearZ); +#endif } - if (c.clear.clearMask & GLR_ASPECT_STENCIL) { + if (c.clear.clearMask & GL_STENCIL) { glClearStencil(c.clear.clearStencil); } + glClear(c.clear.clearMask); break; case GLRRenderCommand::BLENDCOLOR: glBlendColor(c.blendColor.color[0], c.blendColor.color[1], c.blendColor.color[2], c.blendColor.color[3]); @@ -274,6 +285,29 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { curProgram = c.program.program; break; } + case GLRRenderCommand::BIND_INPUT_LAYOUT: + { + GLRInputLayout *layout = c.inputLayout.inputLayout; + for (int i = 0; i < 7; i++) { // SEM_MAX + if (layout->semanticsMask_ & (1 << i)) { + glEnableVertexAttribArray(i); + } + } + for (auto &entry: layout->entries) { + glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, entry.stride, (const void *)(c.inputLayout.offset + entry.offset)); + } + break; + } + case GLRRenderCommand::UNBIND_INPUT_LAYOUT: + { + GLRInputLayout *layout = c.inputLayout.inputLayout; + for (int i = 0; i < 7; i++) { // SEM_MAX + if (layout->semanticsMask_ & (1 << i)) { + glDisableVertexAttribArray(i); + } + } + break; + } case GLRRenderCommand::GENMIPS: glGenerateMipmap(GL_TEXTURE_2D); break; @@ -295,6 +329,8 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } if (activeTexture != GL_TEXTURE0) glActiveTexture(GL_TEXTURE0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } void GLQueueRunner::PerformCopy(const GLRStep &step) { @@ -315,11 +351,11 @@ void GLQueueRunner::PerformCopy(const GLRStep &step) { int depth = 1; switch (step.copy.aspectMask) { - case GLR_ASPECT_COLOR: + case GL_COLOR_BUFFER_BIT: srcTex = src->color.texture; dstTex = dst->color.texture; break; - case GLR_ASPECT_DEPTH: + case GL_DEPTH_BUFFER_BIT: target = GL_RENDERBUFFER; srcTex = src->depth.texture; dstTex = src->depth.texture; diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index e953e840a7..ff8be97b6b 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -18,8 +18,12 @@ struct GLOffset2D { int x, y; }; -class GLRProgram; + +class GLRShader; class GLRTexture; +class GLRBuffer; +class GLRProgram; +class GLRInputLayout; enum class GLRRenderCommand : uint8_t { DEPTH, @@ -35,6 +39,8 @@ enum class GLRRenderCommand : uint8_t { CLEAR, BINDPROGRAM, BINDTEXTURE, + BIND_INPUT_LAYOUT, + UNBIND_INPUT_LAYOUT, GENMIPS, DRAW, DRAW_INDEXED, @@ -109,6 +115,10 @@ struct GLRRenderData { struct { GLRProgram *program; } program; + struct { + GLRInputLayout *inputLayout; + intptr_t offset; + } inputLayout; struct { GLenum wrapS; GLenum wrapT; @@ -137,17 +147,13 @@ enum class GLRInitStepType : uint8_t { CREATE_SHADER, CREATE_PROGRAM, CREATE_BUFFER, + CREATE_INPUT_LAYOUT, TEXTURE_IMAGE, TEXTURE_SUBDATA, BUFFER_SUBDATA, }; -class GLRShader; -class GLRTexture; -class GLRProgram; -class GLRBuffer; - struct GLRInitStep { GLRInitStep(GLRInitStepType _type) : stepType(_type) {} GLRInitStepType stepType; @@ -174,6 +180,9 @@ struct GLRInitStep { int size; GLuint usage; } create_buffer; + struct { + GLRInputLayout *inputLayout; + } create_input_layout; struct { GLRBuffer *buffer; int offset; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index ba43fb1768..ea0f7708dd 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -171,7 +171,14 @@ GLuint GLRenderManager::BindFramebufferAsTexture(GLRFramebuffer *fb, int binding } 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.dstPos = dstPos; + step->copy.aspectMask = aspectMask; + steps_.push_back(step); } void GLRenderManager::BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLRect2D dstRect, int aspectMask, bool filter) { diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index a48103b539..ebec0abddc 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -141,6 +141,8 @@ public: void Take(GLDeleter &other) { shaders = std::move(other.shaders); programs = std::move(other.programs); + buffers = std::move(other.buffers); + textures = std::move(other.textures); } std::vector shaders; @@ -149,6 +151,19 @@ public: std::vector textures; }; +class GLRInputLayout { +public: + struct Entry { + int location; + int count; + GLenum type; + GLboolean normalized; + int stride; + intptr_t offset; + }; + std::vector entries; + int semanticsMask_ = 0; +}; class GLRenderManager { public: @@ -206,6 +221,18 @@ public: return step.create_program.program; } + GLRInputLayout *CreateInputLayout(std::vector &entries) { + GLRInitStep step{ GLRInitStepType::CREATE_PROGRAM }; + assert(shaders.size() <= ARRAY_SIZE(step.create_program.shaders)); + step.create_input_layout.inputLayout = new GLRInputLayout(); + step.create_input_layout.inputLayout->entries = std::move(entries); + for (auto &iter : entries) { + step.create_input_layout.inputLayout->semanticsMask_ |= 1 << iter.location; + } + initSteps_.push_back(step); + return step.create_input_layout.inputLayout; + } + void DeleteShader(GLRShader *shader) { deleter_.shaders.push_back(shader); } @@ -218,6 +245,9 @@ public: void DeleteTexture(GLRTexture *texture) { deleter_.textures.push_back(texture); } + void DeleteInputLayout(GLRInputLayout *inputLayout) { + + } void BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); GLuint BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit, int attachment); @@ -266,6 +296,21 @@ public: curRenderStep_->commands.push_back(data); } + void BindInputLayout(GLRInputLayout *inputLayout, const void *offset) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); + GLRRenderData data{ GLRRenderCommand::BIND_INPUT_LAYOUT }; + data.inputLayout.inputLayout = inputLayout; + data.inputLayout.offset = (intptr_t)offset; + curRenderStep_->commands.push_back(data); + } + + void UnbindInputLayout(GLRInputLayout *inputLayout) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); + GLRRenderData data{ GLRRenderCommand::UNBIND_INPUT_LAYOUT }; + data.inputLayout.inputLayout = inputLayout; + curRenderStep_->commands.push_back(data); + } + void GenerateMipmap() { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData data{ GLRRenderCommand::GENMIPS }; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index b247ca0b96..a7bcd9a7d0 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -294,22 +294,19 @@ bool OpenGLShaderModule::Compile(GLRenderManager *render, ShaderLanguage languag class OpenGLInputLayout : public InputLayout { public: + OpenGLInputLayout(GLRenderManager *render) : render_(render) {} ~OpenGLInputLayout(); void Apply(const void *base = nullptr); void Unapply(); - void Compile(); + void Compile(const InputLayoutDesc &desc); bool RequiresBuffer() { - return id_ != 0; + return false; } - InputLayoutDesc desc; - - int semanticsMask_; // Fast way to check what semantics to enable/disable. - int stride_; - GLuint id_; - bool needsEnable_; - intptr_t lastBase_; + GLRInputLayout *inputLayout_; +private: + GLRenderManager *render_; }; class OpenGLPipeline : public Pipeline { @@ -329,8 +326,6 @@ public: bool LinkShaders(); - int GetUniformLoc(const char *name); - bool RequiresBuffer() override { return inputLayout->RequiresBuffer(); } @@ -382,6 +377,9 @@ public: Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override; Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override; + void BeginFrame() override; + void EndFrame() override; + void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override; void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override; @@ -411,7 +409,7 @@ public: // We render "upside down" to the backbuffer since GL is silly. y = targetHeight_ - (top + height); } - glstate.scissorRect.set(left, y, width, height); + renderManager_.SetScissor({ left, y, width, height }); } void SetViewports(int count, Viewport *viewports) override { @@ -542,10 +540,17 @@ OpenGLContext::~OpenGLContext() { boundSamplers_.clear(); } +void OpenGLContext::BeginFrame() { + renderManager_.BeginFrame(); +} + +void OpenGLContext::EndFrame() { + renderManager_.Finish(); +} + InputLayout *OpenGLContext::CreateInputLayout(const InputLayoutDesc &desc) { - OpenGLInputLayout *fmt = new OpenGLInputLayout(); - fmt->desc = desc; - fmt->Compile(); + OpenGLInputLayout *fmt = new OpenGLInputLayout(&renderManager_); + fmt->Compile(desc); return fmt; } @@ -869,28 +874,6 @@ Texture *OpenGLContext::CreateTexture(const TextureDesc &desc) { return new OpenGLTexture(&renderManager_, desc); } -OpenGLInputLayout::~OpenGLInputLayout() { - if (id_) { - glDeleteVertexArrays(1, &id_); - } -} - -void OpenGLInputLayout::Compile() { - int semMask = 0; - for (int i = 0; i < (int)desc.attributes.size(); i++) { - semMask |= 1 << desc.attributes[i].location; - } - semanticsMask_ = semMask; - - if (gl_extensions.ARB_vertex_array_object && gl_extensions.IsCoreContext) { - glGenVertexArrays(1, &id_); - } else { - id_ = 0; - } - needsEnable_ = true; - lastBase_ = -1; -} - DepthStencilState *OpenGLContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) { OpenGLDepthStencilState *ds = new OpenGLDepthStencilState(); ds->depthTestEnabled = desc.depthTestEnabled; @@ -1138,10 +1121,9 @@ void OpenGLContext::Draw(int vertexCount, int offset) { curPipeline_->inputLayout->Apply(); ApplySamplers(); - glDrawArrays(curPipeline_->prim, offset, vertexCount); + renderManager_.Draw(curPipeline_->prim, offset, vertexCount); curPipeline_->inputLayout->Unapply(); - glBindBuffer(GL_ARRAY_BUFFER, 0); } void OpenGLContext::DrawIndexed(int vertexCount, int offset) { @@ -1154,16 +1136,12 @@ void OpenGLContext::DrawIndexed(int vertexCount, int offset) { glDrawElements(curPipeline_->prim, vertexCount, GL_UNSIGNED_INT, (const void *)(size_t)offset); curPipeline_->inputLayout->Unapply(); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } void OpenGLContext::DrawUP(const void *vdata, int vertexCount) { curPipeline_->inputLayout->Apply(vdata); ApplySamplers(); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDrawArrays(curPipeline_->prim, 0, vertexCount); curPipeline_->inputLayout->Unapply(); @@ -1174,83 +1152,70 @@ void OpenGLContext::Clear(int mask, uint32_t colorval, float depthVal, int stenc Uint8x4ToFloat4(col, colorval); GLuint glMask = 0; if (mask & FBChannel::FB_COLOR_BIT) { - glClearColor(col[0], col[1], col[2], col[3]); glMask |= GL_COLOR_BUFFER_BIT; } if (mask & FBChannel::FB_DEPTH_BIT) { -#if defined(USING_GLES2) - glClearDepthf(depthVal); -#else - glClearDepth(depthVal); -#endif glMask |= GL_DEPTH_BUFFER_BIT; } if (mask & FBChannel::FB_STENCIL_BIT) { - glClearStencil(stencilVal); glMask |= GL_STENCIL_BUFFER_BIT; } - glClear(glMask); + renderManager_.Clear(colorval, depthVal, stencilVal, glMask); } DrawContext *T3DCreateGLContext() { return new OpenGLContext(); } +OpenGLInputLayout::~OpenGLInputLayout() { +} + +void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) { + int semMask = 0; + std::vector entries; + for (auto &attr : desc.attributes) { + GLRInputLayout::Entry entry; + entry.location = attr.location; + entry.stride = (GLsizei)desc.bindings[attr.binding].stride; + entry.offset = attr.offset; + switch (attr.format) { + case DataFormat::R32G32_FLOAT: + entry.count = 2; + entry.type = GL_FLOAT; + entry.normalized = GL_FALSE; + break; + case DataFormat::R32G32B32_FLOAT: + entry.count = 3; + entry.type = GL_FLOAT; + entry.normalized = GL_FALSE; + break; + case DataFormat::R32G32B32A32_FLOAT: + entry.count = 4; + entry.type = GL_FLOAT; + entry.normalized = GL_FALSE; + break; + case DataFormat::R8G8B8A8_UNORM: + entry.count = 4; + entry.type = GL_UNSIGNED_BYTE; + entry.normalized = GL_TRUE; + break; + case DataFormat::UNDEFINED: + default: + ELOG("Thin3DGLVertexFormat: Invalid or unknown component type applied."); + break; + } + + entries.push_back(entry); + } + inputLayout_ = render_->CreateInputLayout(entries); +} + void OpenGLInputLayout::Apply(const void *base) { - if (id_ != 0) { - glBindVertexArray(id_); - } - - if (needsEnable_ || id_ == 0) { - for (int i = 0; i < SEM_MAX; i++) { - if (semanticsMask_ & (1 << i)) { - glEnableVertexAttribArray(i); - } - } - if (id_ != 0) { - needsEnable_ = false; - } - } - - intptr_t b = (intptr_t)base; - if (b != lastBase_) { - for (size_t i = 0; i < desc.attributes.size(); i++) { - GLsizei stride = (GLsizei)desc.bindings[desc.attributes[i].binding].stride; - switch (desc.attributes[i].format) { - case DataFormat::R32G32_FLOAT: - glVertexAttribPointer(desc.attributes[i].location, 2, GL_FLOAT, GL_FALSE, stride, (void *)(b + (intptr_t)desc.attributes[i].offset)); - break; - case DataFormat::R32G32B32_FLOAT: - glVertexAttribPointer(desc.attributes[i].location, 3, GL_FLOAT, GL_FALSE, stride, (void *)(b + (intptr_t)desc.attributes[i].offset)); - break; - case DataFormat::R32G32B32A32_FLOAT: - glVertexAttribPointer(desc.attributes[i].location, 4, GL_FLOAT, GL_FALSE, stride, (void *)(b + (intptr_t)desc.attributes[i].offset)); - break; - case DataFormat::R8G8B8A8_UNORM: - glVertexAttribPointer(desc.attributes[i].location, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void *)(b + (intptr_t)desc.attributes[i].offset)); - break; - case DataFormat::UNDEFINED: - default: - ELOG("Thin3DGLVertexFormat: Invalid or unknown component type applied."); - break; - } - } - if (id_ != 0) { - lastBase_ = b; - } - } + render_->BindInputLayout(inputLayout_, base); } void OpenGLInputLayout::Unapply() { - if (id_ == 0) { - for (int i = 0; i < (int)SEM_MAX; i++) { - if (semanticsMask_ & (1 << i)) { - glDisableVertexAttribArray(i); - } - } - } else { - glBindVertexArray(0); - } + render_->UnbindInputLayout(inputLayout_); } // On PC, we always use GL_DEPTH24_STENCIL8. @@ -1579,12 +1544,12 @@ void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int s int aspect = 0; if (channelBits & FB_COLOR_BIT) { - aspect |= GLR_ASPECT_COLOR; + aspect |= GL_COLOR_BUFFER_BIT; } else if (channelBits & (FB_STENCIL_BIT | FB_DEPTH_BIT)) { if (channelBits & FB_STENCIL_BIT) - aspect |= GLR_ASPECT_STENCIL; + aspect |= GL_STENCIL_BUFFER_BIT; if (channelBits & FB_DEPTH_BIT) - aspect |= GLR_ASPECT_DEPTH; + aspect |= GL_DEPTH_BUFFER_BIT; } renderManager_.CopyFramebuffer(src->framebuffer, GLRect2D{ srcX, srcY, width, height }, dst->framebuffer, GLOffset2D{ dstX, dstY }, aspect); } From e1bb4441d847bb9600402d86e8f201ec3ee66841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 18 Nov 2017 21:51:17 +0100 Subject: [PATCH 05/96] First garbage graphics output! --- ext/native/thin3d/GLQueueRunner.cpp | 34 +++++++++++++++++++++++---- ext/native/thin3d/GLRenderManager.cpp | 4 ++++ ext/native/thin3d/GLRenderManager.h | 11 +++++---- ext/native/thin3d/thin3d_gl.cpp | 22 ++++++++++------- 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index f5275fdc72..0468cdb014 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -19,7 +19,12 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { const GLRInitStep &step = steps[i]; switch (step.stepType) { case GLRInitStepType::CREATE_TEXTURE: + { + GLRTexture *tex = step.create_texture.texture; + glGenTextures(1, &tex->texture); + glBindTexture(tex->target, tex->texture); break; + } case GLRInitStepType::CREATE_BUFFER: { GLRBuffer *buffer = step.create_buffer.buffer; @@ -122,8 +127,14 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { glTexImage2D(tex->target, step.texture_image.level, step.texture_image.internalFormat, step.texture_image.width, step.texture_image.height, 0, step.texture_image.format, step.texture_image.type, step.texture_image.data); delete[] step.texture_image.data; CHECK_GL_ERROR_IF_DEBUG(); + 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_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); break; } + default: + Crash(); } } } @@ -147,6 +158,9 @@ void GLQueueRunner::RunSteps(const std::vector &steps) { case GLRStepType::READBACK_IMAGE: PerformReadbackImage(step); break; + default: + Crash(); + break; } delete steps[i]; } @@ -172,9 +186,6 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { // This is supposed to bind a vulkan render pass to the command buffer. PerformBindFramebufferAsRenderTarget(step); - int curWidth = step.render.framebuffer ? step.render.framebuffer->width : 0; // vulkan_->GetBackbufferWidth(); - int curHeight = step.render.framebuffer ? step.render.framebuffer->height : 0; // vulkan_->GetBackbufferHeight(); - GLRFramebuffer *fb = step.render.framebuffer; GLRProgram *curProgram = nullptr; GLint activeTexture = GL_TEXTURE0; @@ -293,7 +304,8 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glEnableVertexAttribArray(i); } } - for (auto &entry: layout->entries) { + for (int i = 0; i < layout->entries.size(); i++) { + auto &entry = layout->entries[i]; glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, entry.stride, (const void *)(c.inputLayout.offset + entry.offset)); } break; @@ -325,12 +337,26 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, c.textureSampler.magFilter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c.textureSampler.minFilter); break; + case GLRRenderCommand::RASTER: + if (c.raster.cullEnable) { + glEnable(GL_CULL_FACE); + glFrontFace(c.raster.frontFace); + glCullFace(c.raster.cullFace); + } else { + glDisable(GL_CULL_FACE); + } + break; + default: + Crash(); + break; } } + if (activeTexture != GL_TEXTURE0) glActiveTexture(GL_TEXTURE0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisable(GL_SCISSOR_TEST); } void GLQueueRunner::PerformCopy(const GLRStep &step) { diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index ea0f7708dd..ddbaca0a19 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -224,12 +224,14 @@ void GLRenderManager::Finish() { FrameData &frameData = frameData_[curFrame]; if (!useThread_) { frameData.steps = std::move(steps_); + frameData.initSteps = std::move(initSteps_); frameData.type = GLRRunType::END; Run(curFrame); } else { std::unique_lock lock(frameData.pull_mutex); VLOG("PUSH: Frame[%d].readyForRun = true", curFrame); frameData.steps = std::move(steps_); + frameData.initSteps = std::move(initSteps_); frameData.readyForRun = true; frameData.type = GLRRunType::END; frameData.pull_condVar.notify_all(); @@ -239,6 +241,8 @@ void GLRenderManager::Finish() { frameData_[curFrame_].deleter.Take(deleter_); curFrame_++; + if (curFrame_ >= MAX_INFLIGHT_FRAMES) + curFrame_ = 0; insideFrame_ = false; } diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index ebec0abddc..fe18490bd8 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -217,16 +217,16 @@ public: for (int i = 0; i < shaders.size(); i++) { step.create_program.shaders[i] = shaders[i]; } + step.create_program.num_shaders = (int)shaders.size(); initSteps_.push_back(step); return step.create_program.program; } GLRInputLayout *CreateInputLayout(std::vector &entries) { - GLRInitStep step{ GLRInitStepType::CREATE_PROGRAM }; - assert(shaders.size() <= ARRAY_SIZE(step.create_program.shaders)); + GLRInitStep step{ GLRInitStepType::CREATE_INPUT_LAYOUT }; step.create_input_layout.inputLayout = new GLRInputLayout(); - step.create_input_layout.inputLayout->entries = std::move(entries); - for (auto &iter : entries) { + 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); @@ -271,6 +271,7 @@ public: void TextureImage(GLRTexture *texture, int level, int width, int height, GLenum internalFormat, GLenum format, GLenum type, uint8_t *data) { 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; @@ -368,7 +369,7 @@ public: void SetUniformM4x4(const char *name, const float *udata) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); - GLRRenderData data{ GLRRenderCommand::UNIFORM4F }; + GLRRenderData data{ GLRRenderCommand::UNIFORMMATRIX }; data.uniformMatrix4.name = name; memcpy(data.uniformMatrix4.m, udata, sizeof(float) * 16); curRenderStep_->commands.push_back(data); diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index a7bcd9a7d0..8aaff8a5c7 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -618,8 +618,6 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : GLenum target = TypeToTarget(desc.type); tex_ = render->CreateTexture(target, width_, height_); - render->BindTexture(0, tex_); - canWrap_ = isPowerOf2(width_) && isPowerOf2(height_); mipLevels_ = desc.mipLevels; if (!desc.initData.size()) @@ -634,7 +632,6 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : } mipLevels_ = desc.generateMips ? desc.mipLevels : level; - render->SetTextureSampler(GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_LINEAR, mipLevels_ > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); /* #ifdef USING_GLES2 if (gl_extensions.GLES3) { @@ -650,8 +647,6 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : AutoGenMipmaps(); } - // Unbind. - render->BindTexture(0, nullptr); } OpenGLTexture::~OpenGLTexture() { @@ -664,7 +659,7 @@ OpenGLTexture::~OpenGLTexture() { void OpenGLTexture::AutoGenMipmaps() { if (!generatedMips_) { - render_->BindTexture(0, tex_); + // Assumes the texture is bound for editing render_->GenerateMipmap(); generatedMips_ = true; } @@ -1133,8 +1128,8 @@ void OpenGLContext::DrawIndexed(int vertexCount, int offset) { // Note: ibuf binding is stored in the VAO, so call this after binding the fmt. curIBuffer_->Bind(curIBufferOffset_); - glDrawElements(curPipeline_->prim, vertexCount, GL_UNSIGNED_INT, (const void *)(size_t)offset); - + renderManager_.DrawIndexed(curPipeline_->prim, vertexCount, GL_UNSIGNED_INT, (void *)(intptr_t)offset); + curPipeline_->inputLayout->Unapply(); } @@ -1142,7 +1137,7 @@ void OpenGLContext::DrawUP(const void *vdata, int vertexCount) { curPipeline_->inputLayout->Apply(vdata); ApplySamplers(); - glDrawArrays(curPipeline_->prim, 0, vertexCount); + renderManager_.Draw(curPipeline_->prim, 0, vertexCount); curPipeline_->inputLayout->Unapply(); } @@ -1483,6 +1478,14 @@ void OpenGLContext::fbo_unbind() { } void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { + OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; + GLRRenderPassAction color = (GLRRenderPassAction)rp.color; + GLRRenderPassAction depth = (GLRRenderPassAction)rp.depth; + + renderManager_.BindFramebufferAsRenderTarget(fb ? fb->framebuffer : nullptr, color, depth, rp.clearColor, rp.clearDepth, rp.clearStencil); + +#if 0 + CHECK_GL_ERROR_IF_DEBUG(); curFB_ = (OpenGLFramebuffer *)fbo; if (fbo) { @@ -1536,6 +1539,7 @@ void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const Render glstate.stencilMask.restore(); } CHECK_GL_ERROR_IF_DEBUG(); +#endif } void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int srcX, int srcY, int srcZ, Framebuffer *fbdst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) { From a3bcd91a31421b87b1e0b787e332511a808f201b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 18 Nov 2017 22:15:12 +0100 Subject: [PATCH 06/96] Fix clears and textures. Things still broken due to memory overwrites. --- ext/native/thin3d/GLQueueRunner.cpp | 9 +++--- ext/native/thin3d/GLQueueRunner.h | 12 ++++---- ext/native/thin3d/GLRenderManager.cpp | 43 ++++++++++++++++----------- ext/native/thin3d/GLRenderManager.h | 4 +-- ext/native/thin3d/thin3d_gl.cpp | 27 ++++++++--------- 5 files changed, 49 insertions(+), 46 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 0468cdb014..9cf5ee446c 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -176,16 +176,15 @@ void GLQueueRunner::PerformBlit(const GLRStep &step) { void GLQueueRunner::PerformRenderPass(const GLRStep &step) { // Don't execute empty renderpasses. - if (step.commands.empty() && step.render.color == GLRRenderPassAction::KEEP && step.render.depthStencil == GLRRenderPassAction::KEEP) { + if (step.commands.empty()) { // Nothing to do. return; } - glEnable(GL_SCISSOR_TEST); - - // This is supposed to bind a vulkan render pass to the command buffer. PerformBindFramebufferAsRenderTarget(step); + glEnable(GL_SCISSOR_TEST); + GLRFramebuffer *fb = step.render.framebuffer; GLRProgram *curProgram = nullptr; GLint activeTexture = GL_TEXTURE0; @@ -224,7 +223,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glClearDepth(c.clear.clearZ); #endif } - if (c.clear.clearMask & GL_STENCIL) { + if (c.clear.clearMask & GL_STENCIL_BUFFER_BIT) { glClearStencil(c.clear.clearStencil); } glClear(c.clear.clearMask); diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index ff8be97b6b..d684533cf2 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -22,6 +22,7 @@ struct GLOffset2D { class GLRShader; class GLRTexture; class GLRBuffer; +class GLRFramebuffer; class GLRProgram; class GLRInputLayout; @@ -39,6 +40,7 @@ enum class GLRRenderCommand : uint8_t { CLEAR, BINDPROGRAM, BINDTEXTURE, + BIND_FB_TEXTURE, BIND_INPUT_LAYOUT, UNBIND_INPUT_LAYOUT, GENMIPS, @@ -112,6 +114,11 @@ struct GLRRenderData { int slot; GLRTexture *texture; } texture; + struct { + int slot; + GLRFramebuffer *framebuffer; + int aspect; + } bind_fb_texture; struct { GLRProgram *program; } program; @@ -231,11 +238,6 @@ struct GLRStep { union { struct { GLRFramebuffer *framebuffer; - GLRRenderPassAction color; - GLRRenderPassAction depthStencil; - uint32_t clearColor; - float clearDepth; - int clearStencil; int numDraws; } render; struct { diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index ddbaca0a19..7e87089660 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -138,13 +138,6 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende return; } } - if (curRenderStep_ && curRenderStep_->commands.size() == 0 && curRenderStep_->render.color == GLRRenderPassAction::KEEP && curRenderStep_->render.depthStencil == GLRRenderPassAction::KEEP) { - // Can trivially kill the last empty render step. - assert(steps_.back() == curRenderStep_); - delete steps_.back(); - steps_.pop_back(); - curRenderStep_ = nullptr; - } if (curRenderStep_ && curRenderStep_->commands.size() == 0) { VLOG("Empty render step. Usually happens after uploading pixels.."); } @@ -152,22 +145,36 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende GLRStep *step = new GLRStep{ GLRStepType::RENDER }; // 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.clearColor = clearColor; - step->render.clearDepth = clearDepth; - step->render.clearStencil = clearStencil; 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 | GL_STENCIL_BUFFER_BIT; + data.clear.clearZ = clearDepth; + data.clear.clearStencil = clearStencil; + } + if (clearMask) { + data.clear.clearMask = clearMask; + step->commands.push_back(data); + } + curRenderStep_ = step; - curWidth_ = fb ? fb->width : 0; // vulkan_->GetBackbufferWidth(); - curHeight_ = fb ? fb->height : 0; // vulkan_->GetBackbufferHeight(); } -GLuint GLRenderManager::BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit, int attachment) { - // Easy in GL. - return fb->color.texture; +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) { @@ -182,7 +189,7 @@ void GLRenderManager::CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLR } void GLRenderManager::BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLRect2D dstRect, int aspectMask, bool filter) { - + Crash(); } void GLRenderManager::BeginFrame() { diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index fe18490bd8..dc9e724425 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -250,7 +250,7 @@ public: } void BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); - GLuint BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit, int attachment); + 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(GLuint texture, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride); @@ -492,8 +492,6 @@ private: FrameData frameData_[MAX_INFLIGHT_FRAMES]; // Submission time state - int curWidth_; - int curHeight_; bool insideFrame_ = false; GLRStep *curRenderStep_ = nullptr; std::vector steps_; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 8aaff8a5c7..f7309202b2 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -475,7 +475,12 @@ public: } uintptr_t GetNativeObject(NativeObject obj) override { - return 0; + switch (obj) { + case NativeObject::RENDER_MANAGER: + return (uintptr_t)&renderManager_; + default: + return 0; + } } void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override {} @@ -783,6 +788,9 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int return; } + if (stride == 0) + stride = width; + // Make a copy of data with stride eliminated. uint8_t *texData = new uint8_t[width * height * alignment]; for (int y = 0; y < height; y++) { @@ -791,7 +799,6 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int render_->TextureImage(tex_, level, width, height, internalFormat, format, type, texData); } - #ifdef DEBUG_READ_PIXELS // TODO: Make more generic. static void LogReadPixelsError(GLenum error) { @@ -1589,19 +1596,9 @@ bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, in void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int color) { OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; - if (!fb) - return; - if (binding != 0) - glActiveTexture(GL_TEXTURE0 + binding); - switch (channelBit) { - case FB_DEPTH_BIT: - glBindTexture(GL_TEXTURE_2D, fb->z_buffer ? fb->z_buffer : fb->z_stencil_buffer); - case FB_COLOR_BIT: - default: - glBindTexture(GL_TEXTURE_2D, fb->color_texture); - break; - } - glActiveTexture(GL_TEXTURE0); + + GLuint aspect = 0; + renderManager_.BindFramebufferAsTexture(fb->framebuffer, binding, (int)channelBit, color); } OpenGLFramebuffer::~OpenGLFramebuffer() { From 60a966c5ec3e18625564b60ab11c076a271c7a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 19 Nov 2017 00:23:52 +0100 Subject: [PATCH 07/96] PushBuffer added. Thin3d works now on top of GLRenderManager, except framebuffers. --- Common/Vulkan/VulkanMemory.cpp | 2 +- ext/native/thin3d/GLQueueRunner.cpp | 14 ++- ext/native/thin3d/GLQueueRunner.h | 5 + ext/native/thin3d/GLRenderManager.cpp | 84 +++++++++++++- ext/native/thin3d/GLRenderManager.h | 155 ++++++++++++++++++++++++-- ext/native/thin3d/thin3d_gl.cpp | 75 ++++++++----- 6 files changed, 298 insertions(+), 37 deletions(-) diff --git a/Common/Vulkan/VulkanMemory.cpp b/Common/Vulkan/VulkanMemory.cpp index d19467847e..721b97e5b5 100644 --- a/Common/Vulkan/VulkanMemory.cpp +++ b/Common/Vulkan/VulkanMemory.cpp @@ -76,7 +76,7 @@ bool VulkanPushBuffer::AddBuffer() { } buf_ = buffers_.size(); - buffers_.resize(buf_ + 1); + buffers_.resize(buf_ + 1); // Why not push_back? buffers_[buf_] = info; return true; } diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 9cf5ee446c..03c6404fa9 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -36,8 +36,10 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { case GLRInitStepType::BUFFER_SUBDATA: { GLRBuffer *buffer = step.buffer_subdata.buffer; - glBufferSubData(buffer->buffer, step.buffer_subdata.offset, step.buffer_subdata.size, step.buffer_subdata.data); - delete[] step.buffer_subdata.data; + glBindBuffer(GL_ARRAY_BUFFER, buffer->buffer); + glBufferSubData(GL_ARRAY_BUFFER, step.buffer_subdata.offset, step.buffer_subdata.size, step.buffer_subdata.data); + if (step.buffer_subdata.deleteData) + delete[] step.buffer_subdata.data; break; } case GLRInitStepType::CREATE_PROGRAM: @@ -184,6 +186,8 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { PerformBindFramebufferAsRenderTarget(step); glEnable(GL_SCISSOR_TEST); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); GLRFramebuffer *fb = step.render.framebuffer; GLRProgram *curProgram = nullptr; @@ -309,6 +313,12 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } break; } + case GLRRenderCommand::BIND_VERTEX_BUFFER: + { + GLuint buf = c.bind_buffer.buffer ? c.bind_buffer.buffer->buffer : 0; + glBindBuffer(GL_ARRAY_BUFFER, buf); + break; + } case GLRRenderCommand::UNBIND_INPUT_LAYOUT: { GLRInputLayout *layout = c.inputLayout.inputLayout; diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index d684533cf2..ccd07a3c17 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -42,6 +42,7 @@ enum class GLRRenderCommand : uint8_t { BINDTEXTURE, BIND_FB_TEXTURE, BIND_INPUT_LAYOUT, + BIND_VERTEX_BUFFER, UNBIND_INPUT_LAYOUT, GENMIPS, DRAW, @@ -119,6 +120,9 @@ struct GLRRenderData { GLRFramebuffer *framebuffer; int aspect; } bind_fb_texture; + struct { + GLRBuffer *buffer; + } bind_buffer; struct { GLRProgram *program; } program; @@ -195,6 +199,7 @@ struct GLRInitStep { int offset; int size; uint8_t *data; // owned, delete[]-d + bool deleteData; } buffer_subdata; struct { GLRTexture *texture; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 7e87089660..226aed1458 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -341,4 +341,86 @@ void GLRenderManager::Wipe() { delete step; } steps_.clear(); -} \ No newline at end of file +} + +GLPushBuffer::GLPushBuffer(GLRenderManager *render, size_t size) : render_(render), buf_(0), offset_(0), size_(size), writePtr_(nullptr) { + bool res = AddBuffer(); + assert(res); +} + +GLPushBuffer::~GLPushBuffer() { + assert(buffers_.empty()); +} + +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; +} + +bool GLPushBuffer::AddBuffer() { + BufInfo info; + info.deviceMemory = new uint8_t[size_]; + info.buffer = render_->CreateBuffer(GL_ARRAY_BUFFER, 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; +} diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index dc9e724425..b1559fb697 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -18,6 +18,8 @@ struct GLRImage { void GLCreateImage(GLRImage &img, int width, int height, GLint format, bool color); +class GLRInputLayout; + class GLRFramebuffer { public: GLRFramebuffer(int _width, int _height) { @@ -143,12 +145,14 @@ public: programs = std::move(other.programs); buffers = std::move(other.buffers); textures = std::move(other.textures); + inputLayouts = std::move(other.inputLayouts); } std::vector shaders; std::vector programs; std::vector buffers; std::vector textures; + std::vector inputLayouts; }; class GLRInputLayout { @@ -195,6 +199,8 @@ public: GLRBuffer *CreateBuffer(GLuint target, int size, GLuint usage) { GLRInitStep step{ GLRInitStepType::CREATE_BUFFER }; step.create_buffer.buffer = new GLRBuffer(target); + step.create_buffer.size = size; + step.create_buffer.usage = usage; initSteps_.push_back(step); return step.create_buffer.buffer; } @@ -246,7 +252,7 @@ public: deleter_.textures.push_back(texture); } void DeleteInputLayout(GLRInputLayout *inputLayout) { - + deleter_.inputLayouts.push_back(inputLayout); } void BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); @@ -257,8 +263,8 @@ public: 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. - void BufferSubdata(GLRBuffer *buffer, int offset, int size, uint8_t *data) { + // Takes ownership of data if deleteData = true. + void BufferSubdata(GLRBuffer *buffer, int offset, int 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 }; @@ -266,6 +272,7 @@ public: step.buffer_subdata.offset = offset; step.buffer_subdata.size = size; step.buffer_subdata.data = data; + step.buffer_subdata.deleteData = deleteData; initSteps_.push_back(step); } @@ -297,8 +304,16 @@ public: curRenderStep_->commands.push_back(data); } + void BindVertexBuffer(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_VERTEX_BUFFER }; + data.bind_buffer.buffer = buffer; + curRenderStep_->commands.push_back(data); + } + void BindInputLayout(GLRInputLayout *inputLayout, const void *offset) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); + assert(inputLayout); GLRRenderData data{ GLRRenderCommand::BIND_INPUT_LAYOUT }; data.inputLayout.inputLayout = inputLayout; data.inputLayout.offset = (intptr_t)offset; @@ -307,6 +322,7 @@ public: void UnbindInputLayout(GLRInputLayout *inputLayout) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); + assert(inputLayout); GLRRenderData data{ GLRRenderCommand::UNBIND_INPUT_LAYOUT }; data.inputLayout.inputLayout = inputLayout; curRenderStep_->commands.push_back(data); @@ -375,6 +391,20 @@ public: 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 SetStencil(bool enabled, GLenum func, GLenum sFail, GLenum zFail, GLenum pass, uint8_t writeMask, uint8_t compareMask, uint8_t refValue) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData data{ GLRRenderCommand::STENCIL }; @@ -450,6 +480,10 @@ public: enum { MAX_INFLIGHT_FRAMES = 3 }; + int GetCurFrame() const { + return curFrame_; + } + private: void BeginSubmitFrame(int frame); void EndSubmitFrame(int frame); @@ -461,10 +495,6 @@ private: void StopThread(); - int GetCurFrame() const { - return curFrame_; - } - // Per-frame data, round-robin so we can overlap submission with execution of the previous frame. struct FrameData { std::mutex push_mutex; @@ -511,3 +541,114 @@ private: int curFrame_ = 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, 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() { + assert(!writePtr_); + // VkResult res = vkMapMemory(device_, buffers_[buf_].deviceMemory, 0, size_, 0, (void **)(&writePtr_)); + writePtr_ = buffers_[buf_].deviceMemory; + assert(writePtr_); + } + + 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_; + offset_ += (numBytes + 3) & ~3; // Round up to 4 bytes. + + if (offset_ >= size_) { + NextBuffer(numBytes); + out = offset_; + offset_ += (numBytes + 3) & ~3; + } + *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; + +private: + bool AddBuffer(); + void NextBuffer(size_t minSize); + void Defragment(); + + GLRenderManager *render_; + std::vector buffers_; + size_t buf_; + size_t offset_; + size_t size_; + uint8_t *writePtr_; +}; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index f7309202b2..5d3004d3ba 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -176,14 +176,7 @@ public: // uint32_t fixedColor; void Apply(GLRenderManager *render) { - if (enabled) { - glEnable(GL_BLEND); - glBlendEquationSeparate(eqCol, eqAlpha); - glBlendFuncSeparate(srcCol, dstCol, srcAlpha, dstAlpha); - } else { - glDisable(GL_BLEND); - } - glColorMask(colorMask & 1, (colorMask >> 1) & 1, (colorMask >> 2) & 1, (colorMask >> 3) & 1); + render->SetBlendAndMask(colorMask, enabled, srcCol, dstCol, srcAlpha, dstAlpha, eqCol, eqAlpha); } }; @@ -297,14 +290,13 @@ public: OpenGLInputLayout(GLRenderManager *render) : render_(render) {} ~OpenGLInputLayout(); - void Apply(const void *base = nullptr); - void Unapply(); void Compile(const InputLayoutDesc &desc); bool RequiresBuffer() { return false; } GLRInputLayout *inputLayout_; + int stride; private: GLRenderManager *render_; }; @@ -510,6 +502,13 @@ private: // Framebuffer state GLuint currentDrawHandle_ = 0; GLuint currentReadHandle_ = 0; + + // Frames in flight is not such a strict concept as with Vulkan until we start using glBufferStorage and fences. + // But might as well have the structure ready, and can't hurt to rotate buffers. + struct FrameData { + GLPushBuffer *push; + }; + FrameData frameData_[GLRenderManager::MAX_INFLIGHT_FRAMES]; }; OpenGLContext::OpenGLContext() { @@ -538,18 +537,29 @@ OpenGLContext::OpenGLContext() { default: caps_.vendor = GPUVendor::VENDOR_UNKNOWN; break; + } + for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) { + frameData_[i].push = new GLPushBuffer(&renderManager_, 64 * 1024); } } OpenGLContext::~OpenGLContext() { + for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) { + frameData_[i].push->Destroy(); + delete frameData_[i].push; + } boundSamplers_.clear(); } void OpenGLContext::BeginFrame() { renderManager_.BeginFrame(); + FrameData &frameData = frameData_[renderManager_.GetCurFrame()]; + frameData.push->Begin(); } void OpenGLContext::EndFrame() { + FrameData &frameData = frameData_[renderManager_.GetCurFrame()]; + frameData.push->End(); // upload the data! renderManager_.Finish(); } @@ -1120,33 +1130,50 @@ void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) { void OpenGLContext::Draw(int vertexCount, int offset) { curVBuffers_[0]->Bind(curVBufferOffsets_[0]); - curPipeline_->inputLayout->Apply(); + renderManager_.BindInputLayout(curPipeline_->inputLayout->inputLayout_, 0); ApplySamplers(); renderManager_.Draw(curPipeline_->prim, offset, vertexCount); - curPipeline_->inputLayout->Unapply(); + renderManager_.UnbindInputLayout(curPipeline_->inputLayout->inputLayout_); } void OpenGLContext::DrawIndexed(int vertexCount, int offset) { curVBuffers_[0]->Bind(curVBufferOffsets_[0]); - curPipeline_->inputLayout->Apply(); + renderManager_.BindInputLayout(curPipeline_->inputLayout->inputLayout_, 0); ApplySamplers(); // Note: ibuf binding is stored in the VAO, so call this after binding the fmt. curIBuffer_->Bind(curIBufferOffset_); renderManager_.DrawIndexed(curPipeline_->prim, vertexCount, GL_UNSIGNED_INT, (void *)(intptr_t)offset); - - curPipeline_->inputLayout->Unapply(); + renderManager_.UnbindInputLayout(curPipeline_->inputLayout->inputLayout_); } void OpenGLContext::DrawUP(const void *vdata, int vertexCount) { - curPipeline_->inputLayout->Apply(vdata); +#if 1 + int stride = curPipeline_->inputLayout->stride; + size_t dataSize = stride * vertexCount; + + FrameData &frameData = frameData_[renderManager_.GetCurFrame()]; + + GLRBuffer *buf; + size_t offset = frameData.push->Push(vdata, dataSize, &buf); + ApplySamplers(); - renderManager_.Draw(curPipeline_->prim, 0, vertexCount); + renderManager_.BindVertexBuffer(buf); + renderManager_.BindInputLayout(curPipeline_->inputLayout->inputLayout_, (void *)offset); - curPipeline_->inputLayout->Unapply(); + renderManager_.Draw(curPipeline_->prim, 0, vertexCount); + renderManager_.UnbindInputLayout(curPipeline_->inputLayout->inputLayout_); + renderManager_.BindVertexBuffer(nullptr); +#else + ApplySamplers(); + renderManager_.BindInputLayout(curPipeline_->inputLayout->inputLayout_, (void *)vdata); + renderManager_.Draw(curPipeline_->prim, 0, vertexCount); + renderManager_.UnbindInputLayout(curPipeline_->inputLayout->inputLayout_); + +#endif } void OpenGLContext::Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) { @@ -1174,6 +1201,10 @@ OpenGLInputLayout::~OpenGLInputLayout() { void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) { int semMask = 0; + + // This is only accurate if there's only one stream. But whatever, for now. + stride = (GLsizei)desc.bindings[0].stride; + std::vector entries; for (auto &attr : desc.attributes) { GLRInputLayout::Entry entry; @@ -1212,14 +1243,6 @@ void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) { inputLayout_ = render_->CreateInputLayout(entries); } -void OpenGLInputLayout::Apply(const void *base) { - render_->BindInputLayout(inputLayout_, base); -} - -void OpenGLInputLayout::Unapply() { - render_->UnbindInputLayout(inputLayout_); -} - // On PC, we always use GL_DEPTH24_STENCIL8. // On Android, we try to use what's available. From 9340260802a1841d6406883220b3480dd36ef8dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 19 Nov 2017 00:43:58 +0100 Subject: [PATCH 08/96] Finish the shader manager (almost) --- GPU/Common/GPUStateUtils.cpp | 16 +- GPU/GLES/DrawEngineGLES.cpp | 2 +- GPU/GLES/DrawEngineGLES.h | 3 +- GPU/GLES/GPU_GLES.cpp | 6 +- GPU/GLES/ShaderManagerGLES.cpp | 452 ++++++++++------------------ GPU/GLES/ShaderManagerGLES.h | 19 +- GPU/GLES/StateMappingGLES.cpp | 126 +++----- GPU/GLES/TextureCacheGLES.cpp | 27 +- ext/native/thin3d/GLQueueRunner.cpp | 84 +++++- ext/native/thin3d/GLQueueRunner.h | 10 +- ext/native/thin3d/GLRenderManager.h | 65 +++- ext/native/thin3d/thin3d_gl.cpp | 9 +- 12 files changed, 355 insertions(+), 464 deletions(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 6060449807..d1091b9a3e 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -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; diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 7cba85b02b..be77ad5362 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -115,7 +115,7 @@ 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) { decOptions_.expandAllWeightsToFloat = false; decOptions_.expand8BitNormalsToFloat = false; diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index 037758d670..f50b0ffb35 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -101,7 +101,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); @@ -189,6 +189,7 @@ private: TextureCacheGLES *textureCache_ = nullptr; FramebufferManagerGLES *framebufferManager_ = nullptr; FragmentTestCacheGLES *fragmentTestCache_ = nullptr; + Draw::DrawContext *draw_; int bufferDecimationCounter_ = 0; diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index b6f112a0ae..78528e7a19 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -79,11 +79,13 @@ 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) { UpdateVsyncInterval(true); CheckGPUFeatures(); - shaderManagerGL_ = new ShaderManagerGLES(); + GLRenderManager *render = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + + shaderManagerGL_ = new ShaderManagerGLES(render); framebufferManagerGL_ = new FramebufferManagerGLES(draw); framebufferManager_ = framebufferManagerGL_; textureCacheGL_ = new TextureCacheGLES(draw); diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 11ee8d736d..b2a54a3d42 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -26,6 +26,7 @@ #include "base/logging.h" #include "base/timeutil.h" #include "gfx/gl_debug_log.h" +#include "thin3d/GLRenderManager.h" #include "i18n/i18n.h" #include "math/math_util.h" #include "math/lin/matrix4x4.h" @@ -43,8 +44,8 @@ #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, 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,218 +56,131 @@ 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_); } 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 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 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 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); + program = render->CreateProgram(shaders, semantics, queries, gstate_c.featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND); + + render->BindProgram(program); // Default uniform values - glUniform1i(u_tex, 0); - glUniform1i(u_fbotex, 1); - glUniform1i(u_testtex, 2); + render->SetUniformI1(&u_tex, 0); + render->SetUniformI1(&u_fbotex, 1); + render->SetUniformI1(&u_fbotex, 2); if (u_tess_pos_tex != -1) - glUniform1i(u_tess_pos_tex, 4); // Texture unit 4 + render->SetUniformI1(&u_tess_pos_tex, 4); // Texture unit 4 if (u_tess_tex_tex != -1) - glUniform1i(u_tess_tex_tex, 5); // Texture unit 5 + render->SetUniformI1(&u_tess_tex_tex, 5); // Texture unit 5 if (u_tess_col_tex != -1) - glUniform1i(u_tess_col_tex, 6); // Texture unit 6 + render->SetUniformI1(&u_tess_col_tex, 6); // Texture unit 6 // The rest, use the "dirty" mechanism. dirtyUniforms = DIRTY_ALL_UNIFORMS; @@ -274,25 +188,28 @@ LinkedShader::LinkedShader(VShaderID VSID, Shader *vs, FShaderID FSID, Shader *f } 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 +217,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 +225,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 +276,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 +343,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 +354,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 +389,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 +413,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 +435,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 +475,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 +503,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 +537,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 +605,6 @@ void ShaderManagerGLES::DirtyShader() { } void ShaderManagerGLES::DirtyLastShader() { // disables vertex arrays - if (lastShader_) - lastShader_->stop(); lastShader_ = nullptr; lastVShaderSame_ = false; } @@ -748,7 +614,7 @@ Shader *ShaderManagerGLES::CompileFragmentShader(FShaderID FSID) { if (!GenerateFragmentShader(FSID, codeBuffer_, &uniformMask)) { return nullptr; } - return new Shader(FSID, codeBuffer_, GL_FRAGMENT_SHADER, false, 0, uniformMask); + return new Shader(render_, codeBuffer_, GL_FRAGMENT_SHADER, false, 0, uniformMask); } Shader *ShaderManagerGLES::CompileVertexShader(VShaderID VSID) { @@ -756,7 +622,7 @@ 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); + return new Shader(render_, codeBuffer_, GL_VERTEX_SHADER, useHWTransform, attrMask, uniformMask); } Shader *ShaderManagerGLES::ApplyVertexShader(int prim, u32 vertType, VShaderID *VSID) { @@ -806,7 +672,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_, GL_VERTEX_SHADER, false, attrMask, uniformMask); } vsCache_.Insert(*VSID, vs); @@ -860,12 +726,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 +949,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); } diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index fe9c0226da..34ab88a7d8 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -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,9 +125,9 @@ 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, 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 @@ -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 LinkedShaderCache; + GLRenderManager *render_; LinkedShaderCache linkedShaderCache_; bool lastVShaderSame_; diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 195c21d1b9..f810087770 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -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" @@ -123,14 +124,15 @@ 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) { + GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + 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); @@ -163,11 +165,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); + renderManager->SetNoBlendAndMask((colorMask ? 7 : 0) | (alphaMask ? 8 : 0)); #ifndef USING_GLES2 if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) { // Logic Ops @@ -194,11 +195,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 +206,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,18 +229,21 @@ 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); + 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]); #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()]); + //glstate.colorLogicOp.enable(); + //glstate.logicOp.set(logicOps[gstate.getLogicOp()]); } else { - glstate.colorLogicOp.disable(); + //glstate.colorLogicOp.disable(); } } #endif @@ -257,26 +254,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,51 +273,24 @@ 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->SetStencil(gstate.isClearModeAlphaMask() && enableStencilTest, GL_ALWAYS, GL_REPLACE, GL_REPLACE, GL_REPLACE, 0xFF, 0xFF, 0xFF); + 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); - } else { - glstate.stencilTest.disable(); - } + renderManager->SetStencil(stencilState.enabled, compareOps[stencilState.testFunc], + stencilOps[stencilState.sFail], stencilOps[stencilState.zFail], stencilOps[stencilState.zPass], + stencilState.writeMask, stencilState.testMask, stencilState.testRef); } } @@ -340,21 +302,18 @@ 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.scissorY = PSP_CoreParameter().pixelHeight - vpAndScissor.scissorH - vpAndScissor.scissorY; } + renderManager->SetScissor(GLRect2D{ vpAndScissor.scissorX, vpAndScissor.scissorY, vpAndScissor.scissorW, vpAndScissor.scissorH }); 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->SetViewport({ + vpAndScissor.viewportX, vpAndScissor.viewportY, + vpAndScissor.viewportW, vpAndScissor.viewportH, + vpAndScissor.depthRangeMin, vpAndScissor.depthRangeMax }); if (vpAndScissor.dirtyProj) { gstate_c.Dirty(DIRTY_PROJMATRIX); @@ -363,7 +322,6 @@ void DrawEngineGLES::ApplyDrawState(int prim) { gstate_c.Dirty(DIRTY_DEPTHRANGE); } } - CHECK_GL_ERROR_IF_DEBUG(); } void DrawEngineGLES::ApplyDrawStateLate() { @@ -371,32 +329,20 @@ void DrawEngineGLES::ApplyDrawStateLate() { // 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); } - CHECK_GL_ERROR_IF_DEBUG(); } } diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index f9daf9faef..430d4368b3 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -164,25 +164,14 @@ void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) { } } - if (force || entry.minFilt != minFilt) { + if (force || entry.minFilt != minFilt || entry.magFilt != magFilt || entry.sClamp != sClamp || entry.tClamp != tClamp) { 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.minFilt = minFilt; + entry.magFilt = magFilt; + entry.sClamp = sClamp; entry.tClamp = tClamp; } CHECK_GL_ERROR_IF_DEBUG(); @@ -201,6 +190,8 @@ void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferH glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, MinFiltGL[minFilt]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, MagFiltGL[magFilt]); + 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); // 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 +200,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) { @@ -336,14 +324,11 @@ bool SetDebugTexture() { #endif void TextureCacheGLES::BindTexture(TexCacheEntry *entry) { - CHECK_GL_ERROR_IF_DEBUG(); if (entry->textureName != lastBoundTexture) { glBindTexture(GL_TEXTURE_2D, entry->textureName); lastBoundTexture = entry->textureName; } - CHECK_GL_ERROR_IF_DEBUG(); UpdateSamplingParams(*entry, false); - CHECK_GL_ERROR_IF_DEBUG(); } void TextureCacheGLES::Unbind() { diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 03c6404fa9..c186ea065d 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -53,6 +53,24 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { for (auto iter : program->semantics_) { glBindAttribLocation(program->program, iter.location, iter.attrib); } + +#if !defined(USING_GLES2) + if (step.create_program.support_dual_source) { + // Dual source alpha + glBindFragDataLocationIndexed(program->program, 0, 0, "fragColor0"); + glBindFragDataLocationIndexed(program->program, 0, 1, "fragColor1"); + } else if (gl_extensions.VersionGEThan(3, 3, 0)) { + glBindFragDataLocation(program->program, 0, "fragColor0"); + } +#elif !defined(IOS) + if (gl_extensions.GLES3) { + if (gstate_c.featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND) { + glBindFragDataLocationIndexedEXT(program->program, 0, 0, "fragColor0"); + glBindFragDataLocationIndexedEXT(program->program, 0, 1, "fragColor1"); + } + } +#endif + glLinkProgram(program->program); GLint linkStatus = GL_FALSE; @@ -86,6 +104,13 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } } + // Query all the uniforms. + for (int i = 0; i < program->queries_.size(); i++) { + auto &x = program->queries_[i]; + assert(x.name); + *x.dest = glGetUniformLocation(program->program, x.name); + } + // Here we could (using glGetAttribLocation) save a bitmask about which pieces of vertex data are used in the shader // and then AND it with the vertex format bitmask later... } @@ -193,6 +218,8 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { GLRProgram *curProgram = nullptr; GLint activeTexture = GL_TEXTURE0; + int attrMask = 0; + auto &commands = step.commands; for (const auto &c : commands) { switch (c.cmd) { @@ -245,7 +272,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { break; case GLRRenderCommand::UNIFORM4F: { - int loc = c.uniform4.loc; + int loc = c.uniform4.loc ? *c.uniform4.loc : -1; if (c.uniform4.name) { loc = curProgram->GetUniformLoc(c.uniform4.name); } @@ -267,9 +294,33 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } break; } + case GLRRenderCommand::UNIFORM4I: + { + int loc = c.uniform4.loc ? *c.uniform4.loc : -1; + if (c.uniform4.name) { + loc = curProgram->GetUniformLoc(c.uniform4.name); + } + if (loc >= 0) { + switch (c.uniform4.count) { + case 1: + glUniform1iv(loc, 1, (GLint *)&c.uniform4.v[0]); + break; + case 2: + glUniform2iv(loc, 1, (GLint *)c.uniform4.v); + break; + case 3: + glUniform3iv(loc, 1, (GLint *)c.uniform4.v); + break; + case 4: + glUniform4iv(loc, 1, (GLint *)c.uniform4.v); + break; + } + } + break; + } case GLRRenderCommand::UNIFORMMATRIX: { - int loc = c.uniform4.loc; + int loc = c.uniform4.loc ? *c.uniform4.loc : -1; if (c.uniform4.name) { loc = curProgram->GetUniformLoc(c.uniform4.name); } @@ -302,10 +353,16 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { case GLRRenderCommand::BIND_INPUT_LAYOUT: { GLRInputLayout *layout = c.inputLayout.inputLayout; + int enable, disable; + enable = layout->semanticsMask_ & ~attrMask; + disable = (~layout->semanticsMask_) & attrMask; for (int i = 0; i < 7; i++) { // SEM_MAX - if (layout->semanticsMask_ & (1 << i)) { + if (enable & (1 << i)) { glEnableVertexAttribArray(i); } + if (disable & (1 << i)) { + glDisableVertexAttribArray(i); + } } for (int i = 0; i < layout->entries.size(); i++) { auto &entry = layout->entries[i]; @@ -319,16 +376,6 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glBindBuffer(GL_ARRAY_BUFFER, buf); break; } - case GLRRenderCommand::UNBIND_INPUT_LAYOUT: - { - GLRInputLayout *layout = c.inputLayout.inputLayout; - for (int i = 0; i < 7; i++) { // SEM_MAX - if (layout->semanticsMask_ & (1 << i)) { - glDisableVertexAttribArray(i); - } - } - break; - } case GLRRenderCommand::GENMIPS: glGenerateMipmap(GL_TEXTURE_2D); break; @@ -354,6 +401,11 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } else { glDisable(GL_CULL_FACE); } + if (c.raster.ditherEnable) { + glEnable(GL_DITHER); + } else { + glDisable(GL_DITHER); + } break; default: Crash(); @@ -361,6 +413,12 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } } + for (int i = 0; i < 7; i++) { + if (attrMask & (1 << i)) { + glDisableVertexAttribArray(i); + } + } + if (activeTexture != GL_TEXTURE0) glActiveTexture(GL_TEXTURE0); glBindBuffer(GL_ARRAY_BUFFER, 0); diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index ccd07a3c17..f3d5c7df17 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -31,6 +31,7 @@ enum class GLRRenderCommand : uint8_t { STENCIL, BLEND, BLENDCOLOR, + UNIFORM4I, UNIFORM4F, UNIFORMMATRIX, TEXTURESAMPLER, @@ -43,13 +44,14 @@ enum class GLRRenderCommand : uint8_t { BIND_FB_TEXTURE, BIND_INPUT_LAYOUT, BIND_VERTEX_BUFFER, - UNBIND_INPUT_LAYOUT, 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?) struct GLRRenderData { GLRRenderCommand cmd; union { @@ -96,13 +98,13 @@ struct GLRRenderData { } drawIndexed; struct { const char *name; // if null, use loc - GLint 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; + GLint *loc; float m[16]; } uniformMatrix4; struct { @@ -146,6 +148,7 @@ struct GLRRenderData { GLboolean cullEnable; GLenum frontFace; GLenum cullFace; + GLboolean ditherEnable; } raster; }; }; @@ -185,6 +188,7 @@ struct GLRInitStep { GLRProgram *program; GLRShader *shaders[3]; int num_shaders; + bool support_dual_source; } create_program; struct { GLRBuffer *buffer; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index b1559fb697..cd77d12152 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -83,8 +83,15 @@ public: int location; const char *attrib; }; + + struct UniformLocQuery { + GLint *dest; + const char *name; + }; + GLuint program = 0; std::vector semantics_; + std::vector queries_; struct UniformInfo { int loc_; @@ -196,10 +203,10 @@ public: return step.create_texture.texture; } - GLRBuffer *CreateBuffer(GLuint target, int size, GLuint usage) { + GLRBuffer *CreateBuffer(GLuint target, size_t size, GLuint usage) { GLRInitStep step{ GLRInitStepType::CREATE_BUFFER }; step.create_buffer.buffer = new GLRBuffer(target); - step.create_buffer.size = size; + step.create_buffer.size = (int)size; step.create_buffer.usage = usage; initSteps_.push_back(step); return step.create_buffer.buffer; @@ -215,14 +222,23 @@ public: return step.create_shader.shader; } - GLRProgram *CreateProgram(std::vector shaders, std::vector semantics) { + GLRProgram *CreateProgram(std::vector shaders, std::vector semantics, std::vector queries, 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; 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; @@ -320,14 +336,6 @@ public: curRenderStep_->commands.push_back(data); } - void UnbindInputLayout(GLRInputLayout *inputLayout) { - _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); - assert(inputLayout); - GLRRenderData data{ GLRRenderCommand::UNBIND_INPUT_LAYOUT }; - data.inputLayout.inputLayout = inputLayout; - curRenderStep_->commands.push_back(data); - } - void GenerateMipmap() { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData data{ GLRRenderCommand::GENMIPS }; @@ -357,7 +365,25 @@ public: curRenderStep_->commands.push_back(data); } - void SetUniformF(int loc, int count, const float *udata) { + 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; @@ -375,7 +401,7 @@ public: curRenderStep_->commands.push_back(data); } - void SetUniformM4x4(int loc, const float *udata) { + void SetUniformM4x4(GLint *loc, const float *udata) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData data{ GLRRenderCommand::UNIFORM4F }; data.uniformMatrix4.loc = loc; @@ -405,6 +431,14 @@ public: 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); + } + void SetStencil(bool enabled, GLenum func, GLenum sFail, GLenum zFail, GLenum pass, uint8_t writeMask, uint8_t compareMask, uint8_t refValue) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData data{ GLRRenderCommand::STENCIL }; @@ -418,19 +452,20 @@ public: curRenderStep_->commands.push_back(data); } - void SetBlendFactor(float color[4]) { + 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) { + 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); } diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 5d3004d3ba..6ba2d06b38 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -214,7 +214,7 @@ public: class OpenGLRasterState : public RasterState { public: void Apply(GLRenderManager *render) { - render->SetRaster(cullEnable, frontFace, cullMode); + render->SetRaster(cullEnable, frontFace, cullMode, false); } GLboolean cullEnable; @@ -1098,7 +1098,8 @@ bool OpenGLPipeline::LinkShaders() { semantics.push_back({ SEM_NORMAL, "Normal" }); semantics.push_back({ SEM_TANGENT, "Tangent" }); semantics.push_back({ SEM_BINORMAL, "Binormal" }); - program_ = render_->CreateProgram(linkShaders, semantics); + std::vector queries; + program_ = render_->CreateProgram(linkShaders, semantics, queries, false); return true; } @@ -1134,8 +1135,6 @@ void OpenGLContext::Draw(int vertexCount, int offset) { ApplySamplers(); renderManager_.Draw(curPipeline_->prim, offset, vertexCount); - - renderManager_.UnbindInputLayout(curPipeline_->inputLayout->inputLayout_); } void OpenGLContext::DrawIndexed(int vertexCount, int offset) { @@ -1146,7 +1145,6 @@ void OpenGLContext::DrawIndexed(int vertexCount, int offset) { curIBuffer_->Bind(curIBufferOffset_); renderManager_.DrawIndexed(curPipeline_->prim, vertexCount, GL_UNSIGNED_INT, (void *)(intptr_t)offset); - renderManager_.UnbindInputLayout(curPipeline_->inputLayout->inputLayout_); } void OpenGLContext::DrawUP(const void *vdata, int vertexCount) { @@ -1165,7 +1163,6 @@ void OpenGLContext::DrawUP(const void *vdata, int vertexCount) { renderManager_.BindInputLayout(curPipeline_->inputLayout->inputLayout_, (void *)offset); renderManager_.Draw(curPipeline_->prim, 0, vertexCount); - renderManager_.UnbindInputLayout(curPipeline_->inputLayout->inputLayout_); renderManager_.BindVertexBuffer(nullptr); #else ApplySamplers(); From 376d92fcc9685b48f5f06258802668ad99f03e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 19 Nov 2017 12:25:57 +0100 Subject: [PATCH 09/96] Start messing with the draw engine... --- GPU/Common/FramebufferCommon.cpp | 1 + GPU/GLES/DrawEngineGLES.cpp | 174 +++++++++++++++++--------- GPU/GLES/DrawEngineGLES.h | 22 ++++ GPU/GLES/FramebufferManagerGLES.cpp | 4 +- GPU/GLES/FramebufferManagerGLES.h | 3 + GPU/GLES/GPU_GLES.cpp | 10 ++ GPU/GLES/GPU_GLES.h | 1 + GPU/GLES/ShaderManagerGLES.cpp | 22 ++-- GPU/GLES/StateMappingGLES.cpp | 22 ++-- ext/native/thin3d/GLQueueRunner.cpp | 24 +++- ext/native/thin3d/GLQueueRunner.h | 1 + ext/native/thin3d/GLRenderManager.cpp | 8 ++ ext/native/thin3d/GLRenderManager.h | 38 ++++-- ext/native/thin3d/thin3d_gl.cpp | 3 +- 14 files changed, 239 insertions(+), 94 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 94072a85b2..a54e58c61e 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -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 }); } diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index be77ad5362..d05489dd92 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -115,7 +115,8 @@ enum { enum { VAI_KILL_AGE = 120, VAI_UNRELIABLE_KILL_AGE = 240, VAI_UNRELIABLE_KILL_MAX = 4 }; -DrawEngineGLES::DrawEngineGLES(Draw::DrawContext *draw) : vai_(256), draw_(draw) { +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; @@ -178,9 +179,29 @@ void DrawEngineGLES::InitDeviceObjects() { } else { ERROR_LOG(G3D, "Device objects already initialized!"); } + + for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) { + frameData_[i].pushVertex = new GLPushBuffer(render_, 1024 * 1024); + frameData_[i].pushIndex = new GLPushBuffer(render_, 512 * 1024); + } + + int vertexSize = sizeof(TransformedVertex); + std::vector 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() { + for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) { + frameData_[i].pushVertex->Destroy(); + frameData_[i].pushIndex->Destroy(); + delete frameData_[i].pushVertex; + delete frameData_[i].pushIndex; + } + ClearTrackedVertexArrays(); if (!bufferNameCache_.empty()) { glstate.arrayBuffer.unbind(); @@ -194,6 +215,27 @@ void DrawEngineGLES::DestroyDeviceObjects() { glDeleteVertexArrays(1, &sharedVao_); } } + + render_->DeleteInputLayout(softwareInputLayout_); +} + +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(); } struct GlTypeInfo { @@ -220,24 +262,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 &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 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,6 +362,17 @@ 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) { @@ -430,18 +499,23 @@ void DrawEngineGLES::FreeVertexArray(VertexArrayInfo *vai) { void DrawEngineGLES::DoFlush() { PROFILE_THIS_SCOPE("flush"); - CHECK_GL_ERROR_IF_DEBUG(); + FrameData &frameData = frameData_[render_->GetCurFrame()]; + gpuStats.numFlushes++; gpuStats.numTrackedVertexArrays = (int)vai_.size(); 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; @@ -453,6 +527,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); @@ -597,7 +674,7 @@ void DrawEngineGLES::DoFlush() { vai->lastFrame = gpuStats.numFlips; } else { - DecodeVerts(decoded); + DecodeVertsToPushBuffer(frameData.pushVertex, &vertexBufferOffset, &vertexBuffer); rotateVBO: gpuStats.numUncachedVertsDrawn += indexGen.VertexCount(); @@ -606,9 +683,6 @@ rotateVBO: if (!useElements && indexGen.PureCount()) { vertexCount = indexGen.PureCount(); } - glstate.arrayBuffer.unbind(); - glstate.elementArrayBuffer.unbind(); - prim = indexGen.Prim(); } @@ -630,16 +704,21 @@ rotateVBO: } LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, prim); - SetupDecFmtForDraw(program, dec_->GetDecVtxFmt(), vbo ? 0 : decoded); - + GLRInputLayout *inputLayout = SetupDecFmtForDraw(program, dec_->GetDecVtxFmt()); + render_->BindVertexBuffer(vertexBuffer); + render_->BindInputLayout(inputLayout, (void *)(uintptr_t)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); + ; // glDrawElementsInstanced(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); @@ -695,28 +774,18 @@ rotateVBO: 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(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &indexBuffer); + render_->BindIndexBuffer(indexBuffer); + render_->BindVertexBuffer(vertexBuffer); + render_->BindInputLayout(softwareInputLayout_, (void *)(intptr_t)vertexBufferOffset); + render_->DrawIndexed(glprim[prim], numTrans, GL_UNSIGNED_SHORT, inds); } else { - glDrawArrays(glprim[prim], 0, numTrans); + vertexBufferOffset = (uint32_t)frameData.pushVertex->Push(drawBuffer, numTrans * sizeof(TransformedVertex), &vertexBuffer); + render_->BindVertexBuffer(vertexBuffer); + render_->BindInputLayout(softwareInputLayout_, (void *)(intptr_t)vertexBufferOffset); + render_->Draw(glprim[prim], 0, numTrans); } } else if (result.action == SW_CLEAR) { u32 clearColor = result.color; @@ -735,26 +804,12 @@ 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; 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); framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason); int scissorX1 = gstate.getScissorX1(); @@ -766,6 +821,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. } } diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index f50b0ffb35..ce2b51d025 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -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; @@ -126,6 +127,10 @@ public: void ClearTrackedVertexArrays() override; void DecimateTrackedVertexArrays(); + void BeginFrame(); + void EndFrame(); + + // So that this can be inlined void Flush() { if (!numDrawCalls) @@ -151,6 +156,8 @@ public: GLuint BindElementBuffer(const void *p, size_t sz); void DecimateBuffers(); + void ClearInputLayoutMap(); + private: void InitDeviceObjects(); void DestroyDeviceObjects(); @@ -160,14 +167,28 @@ private: void ApplyDrawStateLate(); void ResetShaderBlending(); + GLRInputLayout *SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt); + + void DecodeVertsToPushBuffer(GLPushBuffer *push, uint32_t *bindOffset, GLRBuffer **buf); + GLuint AllocateBuffer(size_t sz); void FreeBuffer(GLuint buf); void FreeVertexArray(VertexArrayInfo *vai); void MarkUnreliable(VertexArrayInfo *vai); + struct FrameData { + GLPushBuffer *pushVertex; + GLPushBuffer *pushIndex; + }; + FrameData frameData_[GLRenderManager::MAX_INFLIGHT_FRAMES]; + PrehashMap vai_; + DenseHashMap inputLayoutMap_; + + GLRInputLayout *softwareInputLayout_ = nullptr; + // Vertex buffer objects // Element buffer objects struct BufferNameInfo { @@ -177,6 +198,7 @@ private: bool used; int lastFrame; }; + GLRenderManager *render_; std::vector bufferNameCache_; std::multimap freeSizedBuffers_; std::unordered_map bufferNameInfo_; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 20eb4aaf8c..bcda525e8a 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -82,6 +82,7 @@ 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(); @@ -92,7 +93,7 @@ void FramebufferManagerGLES::DisableState() { #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); } @@ -247,6 +248,7 @@ FramebufferManagerGLES::FramebufferManagerGLES(Draw::DrawContext *draw) : needBackBufferYSwap_ = true; needGLESRebinds_ = true; CreateDeviceObjects(); + render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); } void FramebufferManagerGLES::Init() { diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index 94c87ae982..d7686a0c96 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -29,6 +29,7 @@ #include "Core/Config.h" #include "GPU/GPUCommon.h" #include "GPU/Common/FramebufferCommon.h" +#include "thin3d/GLRenderManager.h" struct GLSLProgram; class TextureCacheGLES; @@ -110,6 +111,8 @@ private: 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_; diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 78528e7a19..beb5fbbfc4 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -403,6 +403,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); @@ -451,6 +455,12 @@ void GPU_GLES::BeginHostFrame() { shaderManagerGL_->DirtyShader(); textureCacheGL_->NotifyConfigChanged(); } + + drawEngine_.BeginFrame(); +} + +void GPU_GLES::EndHostFrame() { + drawEngine_.EndFrame(); } inline void GPU_GLES::UpdateVsyncInterval(bool force) { diff --git a/GPU/GLES/GPU_GLES.h b/GPU/GLES/GPU_GLES.h index f99fe6d3e3..4378f166a7 100644 --- a/GPU/GLES/GPU_GLES.h +++ b/GPU/GLES/GPU_GLES.h @@ -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; diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index b2a54a3d42..08b9072feb 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -166,21 +166,15 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, attrMask = vs->GetAttrMask(); availableUniforms = vs->GetUniformMask() | fs->GetUniformMask(); - program = render->CreateProgram(shaders, semantics, queries, gstate_c.featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND); + std::vector 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, 4 }); // Texture unit 4 + initialize.push_back({ &u_tess_tex_tex, 5 }); // Texture unit 5 + initialize.push_back({ &u_tess_col_tex, 6 }); // Texture unit 6 - render->BindProgram(program); - - // Default uniform values - render->SetUniformI1(&u_tex, 0); - render->SetUniformI1(&u_fbotex, 1); - render->SetUniformI1(&u_fbotex, 2); - - if (u_tess_pos_tex != -1) - render->SetUniformI1(&u_tess_pos_tex, 4); // Texture unit 4 - if (u_tess_tex_tex != -1) - render->SetUniformI1(&u_tess_tex_tex, 5); // Texture unit 5 - if (u_tess_col_tex != -1) - render->SetUniformI1(&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; diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index f810087770..54acebcb1c 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -230,10 +230,14 @@ void DrawEngineGLES::ApplyDrawState(int prim) { } #endif int mask = (int)rmask | ((int)gmask << 1) | ((int)bmask << 2) | ((int)amask << 3); - 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]); + 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)) { @@ -288,9 +292,13 @@ void DrawEngineGLES::ApplyDrawState(int prim) { GenericStencilFuncState stencilState; ConvertStencilFuncState(stencilState); // Stencil Test - renderManager->SetStencil(stencilState.enabled, compareOps[stencilState.testFunc], - stencilOps[stencilState.sFail], stencilOps[stencilState.zFail], stencilOps[stencilState.zPass], - stencilState.writeMask, stencilState.testMask, stencilState.testRef); + if (stencilState.enabled) { + renderManager->SetStencil(stencilState.enabled, compareOps[stencilState.testFunc], + stencilOps[stencilState.sFail], stencilOps[stencilState.zFail], stencilOps[stencilState.zPass], + stencilState.writeMask, stencilState.testMask, stencilState.testRef); + } else { + renderManager->SetStencilDisabled(); + } } } diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index c186ea065d..0ddc148c0d 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -70,7 +70,6 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } } #endif - glLinkProgram(program->program); GLint linkStatus = GL_FALSE; @@ -111,8 +110,17 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { *x.dest = glGetUniformLocation(program->program, x.name); } - // Here we could (using glGetAttribLocation) save a bitmask about which pieces of vertex data are used in the shader - // and then AND it with the vertex format bitmask later... + // Run initializers. + for (int i = 0; i < program->initialize_.size(); i++) { + auto &init = program->initialize_[i]; + GLint uniform = *init.uniform; + if (uniform != -1) { + switch (init.type) { + case 0: + glUniform1i(uniform, init.value); + } + } + } } break; case GLRInitStepType::CREATE_SHADER: @@ -240,8 +248,11 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } else { glDisable(GL_BLEND); } + glColorMask(c.blend.mask & 1, (c.blend.mask >> 1) & 1, (c.blend.mask >> 2) & 1, (c.blend.mask >> 3) & 1); break; case GLRRenderCommand::CLEAR: + glDisable(GL_SCISSOR_TEST); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); if (c.clear.clearMask & GL_COLOR_BUFFER_BIT) { float color[4]; Uint8x4ToFloat4(color, c.clear.clearColor); @@ -258,6 +269,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glClearStencil(c.clear.clearStencil); } glClear(c.clear.clearMask); + glEnable(GL_SCISSOR_TEST); break; case GLRRenderCommand::BLENDCOLOR: glBlendColor(c.blendColor.color[0], c.blendColor.color[1], c.blendColor.color[2], c.blendColor.color[3]); @@ -376,6 +388,12 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glBindBuffer(GL_ARRAY_BUFFER, buf); break; } + case GLRRenderCommand::BIND_INDEX_BUFFER: + { + GLuint buf = c.bind_buffer.buffer ? c.bind_buffer.buffer->buffer : 0; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf); + break; + } case GLRRenderCommand::GENMIPS: glGenerateMipmap(GL_TEXTURE_2D); break; diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index f3d5c7df17..810027d461 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -44,6 +44,7 @@ enum class GLRRenderCommand : uint8_t { BIND_FB_TEXTURE, BIND_INPUT_LAYOUT, BIND_VERTEX_BUFFER, + BIND_INDEX_BUFFER, GENMIPS, DRAW, DRAW_INDEXED, diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 226aed1458..1c05cc4577 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -352,6 +352,14 @@ 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. diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index cd77d12152..a3ee244b9f 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -89,9 +89,16 @@ public: const char *name; }; + struct Initializer { + GLint *uniform; + int type; + int value; + }; + GLuint program = 0; std::vector semantics_; std::vector queries_; + std::vector initialize_; struct UniformInfo { int loc_; @@ -222,12 +229,15 @@ public: return step.create_shader.shader; } - GLRProgram *CreateProgram(std::vector shaders, std::vector semantics, std::vector queries, bool supportDualSource) { + GLRProgram *CreateProgram( + std::vector shaders, std::vector semantics, std::vector queries, + std::vector 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; for (int i = 0; i < shaders.size(); i++) { step.create_program.shaders[i] = shaders[i]; } @@ -327,6 +337,13 @@ public: 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_INDEX_BUFFER}; + data.bind_buffer.buffer = buffer; + curRenderStep_->commands.push_back(data); + } + void BindInputLayout(GLRInputLayout *inputLayout, const void *offset) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); assert(inputLayout); @@ -403,7 +420,7 @@ public: void SetUniformM4x4(GLint *loc, const float *udata) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); - GLRRenderData data{ GLRRenderCommand::UNIFORM4F }; + GLRRenderData data{ GLRRenderCommand::UNIFORMMATRIX }; data.uniformMatrix4.loc = loc; memcpy(data.uniformMatrix4.m, udata, sizeof(float) * 16); curRenderStep_->commands.push_back(data); @@ -442,6 +459,7 @@ public: void SetStencil(bool enabled, GLenum func, GLenum sFail, GLenum zFail, GLenum pass, uint8_t writeMask, uint8_t compareMask, uint8_t refValue) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData data{ GLRRenderCommand::STENCIL }; + data.stencil.enabled = enabled; data.stencil.func = func; data.stencil.sFail = sFail; data.stencil.zFail = zFail; @@ -452,6 +470,14 @@ public: curRenderStep_->commands.push_back(data); } + void SetStencilDisabled() { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); + GLRRenderData data; + data.cmd = GLRRenderCommand::STENCIL; + data.stencil.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 }; @@ -613,13 +639,7 @@ public: Unmap(); } - void Map() { - assert(!writePtr_); - // VkResult res = vkMapMemory(device_, buffers_[buf_].deviceMemory, 0, size_, 0, (void **)(&writePtr_)); - writePtr_ = buffers_[buf_].deviceMemory; - assert(writePtr_); - } - + void Map(); void Unmap(); // When using the returned memory, make sure to bind the returned vkbuf. diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 6ba2d06b38..1331a34f2d 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -1099,7 +1099,8 @@ bool OpenGLPipeline::LinkShaders() { semantics.push_back({ SEM_TANGENT, "Tangent" }); semantics.push_back({ SEM_BINORMAL, "Binormal" }); std::vector queries; - program_ = render_->CreateProgram(linkShaders, semantics, queries, false); + std::vector initialize; + program_ = render_->CreateProgram(linkShaders, semantics, queries, initialize, false); return true; } From bd6818198ad49e42f82a1fdc711289ec51592c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 19 Nov 2017 17:37:56 +0100 Subject: [PATCH 10/96] More GLRenderManager --- GPU/Common/TextureCacheCommon.cpp | 2 +- GPU/Common/TextureCacheCommon.h | 4 +- GPU/GLES/DepalettizeShaderGLES.cpp | 116 ++++++++------------------ GPU/GLES/DepalettizeShaderGLES.h | 17 ++-- GPU/GLES/DrawEngineGLES.h | 2 +- GPU/GLES/FragmentTestCacheGLES.cpp | 38 +++------ GPU/GLES/FragmentTestCacheGLES.h | 13 +-- GPU/GLES/FramebufferManagerGLES.cpp | 16 ++-- GPU/GLES/FramebufferManagerGLES.h | 2 +- GPU/GLES/GPU_GLES.cpp | 2 +- GPU/GLES/TextureCacheGLES.cpp | 67 +++++---------- GPU/GLES/TextureCacheGLES.h | 13 ++- Windows/GPU/WindowsGLContext.cpp | 2 +- ext/native/thin3d/GLQueueRunner.cpp | 52 ++++++++++-- ext/native/thin3d/GLQueueRunner.h | 17 +++- ext/native/thin3d/GLRenderManager.cpp | 11 +++ ext/native/thin3d/GLRenderManager.h | 41 ++++----- ext/native/thin3d/thin3d_gl.cpp | 30 ++++--- 18 files changed, 210 insertions(+), 235 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 1485a333a3..bcfa89ed31 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -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 >> 8) & 15); } else { entry->framesUntilNextFullHash = entry->numFrames; } diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 31aa6eeb97..aa69f123a5 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -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; }; diff --git a/GPU/GLES/DepalettizeShaderGLES.cpp b/GPU/GLES/DepalettizeShaderGLES.cpp index 71ef33d4c2..7e56ce8525 100644 --- a/GPU/GLES/DepalettizeShaderGLES.cpp +++ b/GPU/GLES/DepalettizeShaderGLES.cpp @@ -89,7 +89,8 @@ static bool CheckShaderCompileSuccess(GLuint shader, const char *code) { } } -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 +103,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); + 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 +121,11 @@ 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); + render_->TextureImage(tex->texture, 0, texturePixels, 1, components, components2, dstFmt, (uint8_t *)rawClut, false); tex->lastFrame = gpuStats.numFlips; texCache_[clutId] = tex; @@ -153,20 +134,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 +155,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 { @@ -201,64 +182,35 @@ 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); DepalShader *depal = new DepalShader(); + + std::vector semantics; + semantics.push_back({ 0, "a_position" }); + semantics.push_back({ 1, "a_texcoord0" }); + + std::vector queries; + queries.push_back({ &depal->u_tex, "tex" }); + queries.push_back({ &depal->u_pal, "pal" }); + + std::vector initializer; + initializer.push_back({ &depal->u_tex, 0 }); + initializer.push_back({ &depal->u_pal, 3 }); + + std::vector shaders{ vertexShader_, fragShader }; + + GLRProgram *program = render_->CreateProgram(shaders, semantics, queries, initializer, false); + depal->program = program; depal->fragShader = fragShader; depal->code = buffer; + depal->a_position = 0; + depal->a_texcoord0 = 1; 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; } diff --git a/GPU/GLES/DepalettizeShaderGLES.h b/GPU/GLES/DepalettizeShaderGLES.h index a32acb2cb3..bc3e93facd 100644 --- a/GPU/GLES/DepalettizeShaderGLES.h +++ b/GPU/GLES/DepalettizeShaderGLES.h @@ -19,34 +19,38 @@ #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; + GLRProgram *program; + GLRShader *fragShader; GLint a_position; GLint a_texcoord0; + 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 DebugGetShaderIDs(DebugShaderType type); @@ -55,9 +59,10 @@ public: private: bool CreateVertexShader(); + GLRenderManager *render_; bool useGL3_; bool vertexShaderFailed_; - GLuint vertexShader_; + GLRShader *vertexShader_; std::map cache_; std::map texCache_; }; diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index ce2b51d025..2ed6046b96 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -141,7 +141,7 @@ public: void FinishDeferred() { if (!numDrawCalls) return; - DecodeVerts(decoded); + DoFlush(); } bool IsCodePtrVertexDecoder(const u8 *ptr) const; diff --git a/GPU/GLES/FragmentTestCacheGLES.cpp b/GPU/GLES/FragmentTestCacheGLES.cpp index e49f336e13..0228f3896e 100644 --- a/GPU/GLES/FragmentTestCacheGLES.cpp +++ b/GPU/GLES/FragmentTestCacheGLES.cpp @@ -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,7 +27,8 @@ static const int FRAGTEST_TEXTURE_OLD_AGE = 307; static const int FRAGTEST_DECIMATION_INTERVAL = 113; -FragmentTestCacheGLES::FragmentTestCacheGLES() : textureCache_(NULL), lastTexture_(0), decimationCounter_(0) { +FragmentTestCacheGLES::FragmentTestCacheGLES(Draw::DrawContext *draw) { + render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); scratchpad_ = new u8[256 * 4]; } @@ -51,17 +53,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(unit, tex); lastTexture_ = tex; return; } @@ -79,13 +76,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(unit, tex); FragmentTestTexture item; item.lastFrame = gpuStats.numFlips; item.texture = tex; @@ -106,7 +99,7 @@ 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]) { // TODO: Might it be better to use GL_ALPHA for simple textures? // TODO: Experiment with 4-bit/etc. textures. @@ -146,22 +139,15 @@ GLuint FragmentTestCacheGLES::CreateTestTexture(const GEComparison funcs[4], con } } - 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, scratchpad_); 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 +158,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; diff --git a/GPU/GLES/FragmentTestCacheGLES.h b/GPU/GLES/FragmentTestCacheGLES.h index 106258e6d0..95eecb23cb 100644 --- a/GPU/GLES/FragmentTestCacheGLES.h +++ b/GPU/GLES/FragmentTestCacheGLES.h @@ -53,13 +53,13 @@ struct FragmentTestID { }; struct FragmentTestTexture { - GLuint texture; + GLRTexture *texture; int lastFrame; }; class FragmentTestCacheGLES { public: - FragmentTestCacheGLES(); + FragmentTestCacheGLES(Draw::DrawContext *draw); ~FragmentTestCacheGLES(); void SetTextureCache(TextureCacheGLES *tc) { @@ -73,13 +73,14 @@ public: 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 cache_; - u8 *scratchpad_; - GLuint lastTexture_; - int decimationCounter_; + u8 *scratchpad_ = nullptr; + GLRTexture *lastTexture_ = nullptr; + int decimationCounter_ = 0; }; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index bcda525e8a..fccf90b1f3 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -287,9 +287,9 @@ void FramebufferManagerGLES::DestroyDeviceObjects() { postShaderProgram_ = nullptr; } if (drawPixelsTex_) { - glDeleteTextures(1, &drawPixelsTex_); - drawPixelsTex_ = 0; - } + render_->DeleteTexture(drawPixelsTex_); + drawPixelsTex_ = 0; + } if (stencilUploadProgram_) { glsl_destroy(stencilUploadProgram_); stencilUploadProgram_ = nullptr; @@ -320,15 +320,16 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma } if (drawPixelsTex_ && (drawPixelsTexFormat_ != srcPixelFormat || drawPixelsTexW_ != texWidth || drawPixelsTexH_ != height)) { - glDeleteTextures(1, &drawPixelsTex_); - drawPixelsTex_ = 0; + render_->DeleteTexture(drawPixelsTex_); + drawPixelsTex_ = nullptr; } if (!drawPixelsTex_) { - drawPixelsTex_ = textureCacheGL_->AllocTextureName(); + 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); @@ -338,9 +339,10 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma 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_); + render_->BindTexture(0, drawPixelsTex_); } // TODO: We can just change the texture format and flip some bits around instead of this. diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index d7686a0c96..42d675ebe9 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -114,7 +114,7 @@ private: GLRenderManager *render_; // Used by DrawPixels - unsigned int drawPixelsTex_; + GLRTexture *drawPixelsTex_; GEBufferFormat drawPixelsTexFormat_; int drawPixelsTexW_; int drawPixelsTexH_; diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index beb5fbbfc4..594c980f05 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -79,7 +79,7 @@ static const GLESCommandTableEntry commandTable[] = { GPU_GLES::CommandInfo GPU_GLES::cmdInfo_[256]; GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) -: GPUCommon(gfxCtx, draw), drawEngine_(draw) { +: GPUCommon(gfxCtx, draw), drawEngine_(draw), fragmentTestCache_(draw), depalShaderCache_(draw) { UpdateVsyncInterval(true); CheckGPUFeatures(); diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 430d4368b3..b841a32f23 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -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" @@ -48,14 +49,12 @@ #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; @@ -73,21 +72,15 @@ void TextureCacheGLES::SetFramebufferManager(FramebufferManagerGLES *fbManager) void TextureCacheGLES::ReleaseTexture(TexCacheEntry *entry, bool delete_them) { DEBUG_LOG(G3D, "Deleting texture %i", entry->textureName); 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) { @@ -165,10 +158,8 @@ void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) { } if (force || entry.minFilt != minFilt || entry.magFilt != magFilt || entry.sClamp != sClamp || entry.tClamp != tClamp) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, MinFiltGL[minFilt]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, MagFiltGL[magFilt]); - 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); + 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); entry.minFilt = minFilt; entry.magFilt = magFilt; entry.sClamp = sClamp; @@ -188,10 +179,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]); - 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); + 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. @@ -325,14 +314,14 @@ bool SetDebugTexture() { void TextureCacheGLES::BindTexture(TexCacheEntry *entry) { if (entry->textureName != lastBoundTexture) { - glBindTexture(GL_TEXTURE_2D, entry->textureName); + render_->BindTexture(0, entry->textureName); lastBoundTexture = entry->textureName; } UpdateSamplingParams(*entry, false); } void TextureCacheGLES::Unbind() { - glBindTexture(GL_TEXTURE_2D, 0); + render_->BindTexture(0, nullptr); InvalidateLastTexture(); } @@ -413,8 +402,8 @@ public: } } - void Use(DrawEngineGLES *transformDraw) { - glUseProgram(shader_->program); + void Use(GLRenderManager *render, DrawEngineGLES *transformDraw) { + render->BindProgram(shader_->program); // Restore will rebind all of the state below. if (gstate_c.Supports(GPU_SUPPORTS_VAO)) { @@ -476,18 +465,16 @@ 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_); + shaderApply.Use(render_, drawEngine_); - glActiveTexture(GL_TEXTURE3); - glBindTexture(GL_TEXTURE_2D, clutTexture); - glActiveTexture(GL_TEXTURE0); + render_->BindTexture(3, clutTexture); framebufferManagerGL_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_SKIP_COPY); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -551,7 +538,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); } } @@ -615,8 +602,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; } @@ -648,7 +635,7 @@ 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 @@ -727,7 +714,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag if (gstate_c.Supports(GPU_SUPPORTS_ANISOTROPY)) { int aniso = 1 << g_Config.iAnisotropyLevel; - float anisotropyLevel = (float) aniso > maxAnisotropyLevel ? maxAnisotropyLevel : (float) aniso; + float anisotropyLevel = aniso; //(float) aniso > maxAnisotropyLevel ? maxAnisotropyLevel : (float) aniso; if (anisotropyLevel > 1.0f) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropyLevel); } @@ -743,16 +730,6 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag 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 { switch (format) { case GE_TFMT_CLUT4: diff --git a/GPU/GLES/TextureCacheGLES.h b/GPU/GLES/TextureCacheGLES.h index 9e894e2476..559ccf7720 100644 --- a/GPU/GLES/TextureCacheGLES.h +++ b/GPU/GLES/TextureCacheGLES.h @@ -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); @@ -87,12 +87,11 @@ private: void BuildTexture(TexCacheEntry *const entry, bool replaceImages) override; - std::vector nameCache_; + GLRenderManager *render_; TextureScalerGLES scaler; - u32 lastBoundTexture; - float maxAnisotropyLevel; + GLRTexture *lastBoundTexture; FramebufferManagerGLES *framebufferManagerGL_; DepalShaderCacheGLES *depalShaderCache_; diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index 3b9707ca4c..283797b064 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -121,7 +121,7 @@ void FormatDebugOutputARB(char outStr[], size_t outStrSize, GLenum source, GLenu _snprintf(severityStr, 32, severityFmt, severity); - _snprintf(outStr, outStrSize, "OpenGL: %s [source=%s type=%s severity=%s id=%d]", msg, sourceStr, typeStr, severityStr, id); + _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, diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 0ddc148c0d..37084abb05 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -6,12 +6,19 @@ #include "gfx_es2/gpu_features.h" #include "math/dataconv.h" -void GLQueueRunner::CreateDeviceObjects() { +#define TEXCACHE_NAME_CACHE_SIZE 16 +void GLQueueRunner::CreateDeviceObjects() { + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyLevel_); + glGenVertexArrays(1, &globalVAO_); } void GLQueueRunner::DestroyDeviceObjects() { - + if (!nameCache_.empty()) { + glDeleteTextures((GLsizei)nameCache_.size(), &nameCache_[0]); + nameCache_.clear(); + } + glDeleteVertexArrays(1, &globalVAO_); } void GLQueueRunner::RunInitSteps(const std::vector &steps) { @@ -146,10 +153,14 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } break; } - case GLRInitStepType::CREATE_INPUT_LAYOUT: { GLRInputLayout *layout = step.create_input_layout.inputLayout; + // Nothing to do unless we want to create vertexbuffer objects (GL 4.5) + break; + } + case GLRInitStepType::CREATE_FRAMEBUFFER: + { // TODO break; } @@ -164,8 +175,8 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { CHECK_GL_ERROR_IF_DEBUG(); 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_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST); break; } default: @@ -219,6 +230,8 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { PerformBindFramebufferAsRenderTarget(step); glEnable(GL_SCISSOR_TEST); + + glBindVertexArray(globalVAO_); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); @@ -342,15 +355,20 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { break; } case GLRRenderCommand::STENCIL: - glStencilFunc(c.stencil.func, c.stencil.ref, c.stencil.compareMask); - glStencilOp(c.stencil.sFail, c.stencil.zFail, c.stencil.pass); - glStencilMask(c.stencil.writeMask); + if (c.stencil.enabled) { + glEnable(GL_STENCIL_TEST); + glStencilFunc(c.stencil.func, c.stencil.ref, c.stencil.compareMask); + glStencilOp(c.stencil.sFail, c.stencil.zFail, c.stencil.pass); + glStencilMask(c.stencil.writeMask); + } else { + glDisable(GL_STENCIL_TEST); + } break; case GLRRenderCommand::BINDTEXTURE: { GLint target = c.texture.slot; if (target != activeTexture) { - glActiveTexture(target); + glActiveTexture(GL_TEXTURE0 + target); activeTexture = target; } glBindTexture(c.texture.texture->target, c.texture.texture->texture); @@ -376,6 +394,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glDisableVertexAttribArray(i); } } + attrMask = layout->semanticsMask_; for (int i = 0; i < layout->entries.size(); i++) { auto &entry = layout->entries[i]; glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, entry.stride, (const void *)(c.inputLayout.offset + entry.offset)); @@ -410,6 +429,9 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c.textureSampler.wrapT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, c.textureSampler.magFilter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c.textureSampler.minFilter); + if (c.textureSampler.anisotropy != 0.0f) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, c.textureSampler.anisotropy); + } break; case GLRRenderCommand::RASTER: if (c.raster.cullEnable) { @@ -441,6 +463,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glActiveTexture(GL_TEXTURE0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); glDisable(GL_SCISSOR_TEST); } @@ -510,3 +533,14 @@ void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) { void GLQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) { } + +GLuint GLQueueRunner::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; +} + diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index 810027d461..2d96cf0fae 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -138,6 +138,7 @@ struct GLRRenderData { GLenum wrapT; GLenum magFilter; GLenum minFilter; // also includes mip. GL... + float anisotropy; } textureSampler; struct { GLRViewport vp; @@ -163,6 +164,7 @@ enum class GLRInitStepType : uint8_t { CREATE_PROGRAM, CREATE_BUFFER, CREATE_INPUT_LAYOUT, + CREATE_FRAMEBUFFER, TEXTURE_IMAGE, TEXTURE_SUBDATA, @@ -176,9 +178,6 @@ struct GLRInitStep { struct { GLRTexture *texture; GLenum target; - int width; - int height; - // ... } create_texture; struct { GLRShader *shader; @@ -199,6 +198,9 @@ struct GLRInitStep { struct { GLRInputLayout *inputLayout; } create_input_layout; + struct { + GLRFramebuffer *framebuffer; + } create_framebuffer; struct { GLRBuffer *buffer; int offset; @@ -214,6 +216,7 @@ struct GLRInitStep { int level; int width; int height; + bool linearFilter; uint8_t *data; // owned, delete[]-d } texture_image; }; @@ -312,10 +315,18 @@ private: void ResizeReadbackBuffer(size_t requiredSize); + GLuint globalVAO_; + GLint curFramebuffer_ = 0; // Readback buffer. Currently we only support synchronous readback, so we only really need one. // We size it generously. GLint readbackBuffer_ = 0; int readbackBufferSize_ = 0; + + float maxAnisotropyLevel_; + + GLuint AllocTextureName(); + // Texture name cache. Ripped straight from TextureCacheGLES. + std::vector nameCache_; }; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 1c05cc4577..301418449d 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -28,12 +28,19 @@ GLRenderManager::GLRenderManager() { for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { } + + if (!useThread_) { + queueRunner_.CreateDeviceObjects(); + } } GLRenderManager::~GLRenderManager() { for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { } + if (!useThread_) { + queueRunner_.DestroyDeviceObjects(); + } } void GLRenderManager::ThreadFunc() { @@ -41,6 +48,7 @@ void GLRenderManager::ThreadFunc() { int threadFrame = threadInitFrame_; bool nextFrame = false; bool firstFrame = true; + queueRunner_.CreateDeviceObjects(); while (true) { { if (nextFrame) { @@ -75,6 +83,7 @@ void GLRenderManager::ThreadFunc() { Run(threadFrame); VLOG("PULL: Finished frame %d", threadFrame); } + queueRunner_.DestroyDeviceObjects(); VLOG("PULL: Quitting"); } @@ -365,6 +374,8 @@ void GLPushBuffer::Unmap() { // 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. + if (offset_ > 0 && buffers_[buf_].deviceMemory[0] == 0xCD) + Crash(); render_->BufferSubdata(buffers_[buf_].buffer, 0, offset_, buffers_[buf_].deviceMemory, false); writePtr_ = nullptr; } diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index a3ee244b9f..4c08c36e05 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -22,27 +22,8 @@ class GLRInputLayout; class GLRFramebuffer { public: - GLRFramebuffer(int _width, int _height) { - width = _width; - height = _height; - - /* - CreateImage(vulkan_, initCmd, color, width, height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true); - CreateImage(vulkan_, initCmd, depth, width, height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false); - - VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - VkImageView views[2]{}; - - fbci.renderPass = renderPass; - fbci.attachmentCount = 2; - fbci.pAttachments = views; - views[0] = color.imageView; - views[1] = depth.imageView; - fbci.width = width; - fbci.height = height; - fbci.layers = 1; - - vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf);*/ + GLRFramebuffer(int _width, int _height, bool z_stencil) + : width(_width), height(_height), z_stencil_(z_stencil) { } ~GLRFramebuffer() { @@ -57,6 +38,7 @@ public: GLRImage depth{}; int width = 0; int height = 0; + 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 @@ -200,12 +182,10 @@ public: void Wipe(); // Creation commands. These were not needed in Vulkan since there we can do that on the main thread. - GLRTexture *CreateTexture(GLenum target, int w, int h) { + GLRTexture *CreateTexture(GLenum target) { GLRInitStep step{ GLRInitStepType::CREATE_TEXTURE }; step.create_texture.texture = new GLRTexture(); step.create_texture.texture->target = target; - step.create_texture.width = w; - step.create_texture.height = h; initSteps_.push_back(step); return step.create_texture.texture; } @@ -229,6 +209,13 @@ public: 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; + } + GLRProgram *CreateProgram( std::vector shaders, std::vector semantics, std::vector queries, std::vector initalizers, bool supportDualSource) { @@ -302,7 +289,7 @@ public: initSteps_.push_back(step); } - void TextureImage(GLRTexture *texture, int level, int width, int height, GLenum internalFormat, GLenum format, GLenum type, uint8_t *data) { + 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; @@ -312,6 +299,7 @@ public: step.texture_image.level = level; step.texture_image.width = width; step.texture_image.height = height; + step.texture_image.linearFilter = linearFilter; initSteps_.push_back(step); } @@ -496,13 +484,14 @@ public: } // Modifies the current texture as per GL specs, not global state. - void SetTextureSampler(GLenum wrapS, GLenum wrapT, GLenum magFilter, GLenum minFilter) { + 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); } diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 1331a34f2d..33a890d90d 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -631,7 +631,7 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : format_ = desc.format; type_ = desc.type; GLenum target = TypeToTarget(desc.type); - tex_ = render->CreateTexture(target, width_, height_); + tex_ = render->CreateTexture(target); canWrap_ = isPowerOf2(width_) && isPowerOf2(height_); mipLevels_ = desc.mipLevels; @@ -1070,7 +1070,7 @@ void OpenGLContext::ApplySamplers() { } GLenum magFilt = samp->magFilt; GLenum minFilt = tex->HasMips() ? samp->mipMinFilt : samp->minFilt; - renderManager_.SetTextureSampler(wrapS, wrapT, magFilt, minFilt); + renderManager_.SetTextureSampler(wrapS, wrapT, magFilt, minFilt, 0.0f); } } } @@ -1325,6 +1325,9 @@ OpenGLFramebuffer *OpenGLContext::fbo_ext_create(const FramebufferDesc &desc) { Framebuffer *OpenGLContext::CreateFramebuffer(const FramebufferDesc &desc) { CheckGLExtensions(); + OpenGLFramebuffer *fbo = new OpenGLFramebuffer(); + // fbo->framebuffer = renderManager_.CreateFramebuffer() + #ifndef USING_GLES2 if (!gl_extensions.ARB_framebuffer_object && gl_extensions.EXT_framebuffer_object) { return fbo_ext_create(desc); @@ -1335,7 +1338,6 @@ Framebuffer *OpenGLContext::CreateFramebuffer(const FramebufferDesc &desc) { #endif CHECK_GL_ERROR_IF_DEBUG(); - OpenGLFramebuffer *fbo = new OpenGLFramebuffer(); fbo->width = desc.width; fbo->height = desc.height; fbo->colorDepth = desc.colorDepth; @@ -1578,10 +1580,10 @@ void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int s if (channelBits & FB_COLOR_BIT) { aspect |= GL_COLOR_BUFFER_BIT; } else if (channelBits & (FB_STENCIL_BIT | FB_DEPTH_BIT)) { - if (channelBits & FB_STENCIL_BIT) - aspect |= GL_STENCIL_BUFFER_BIT; if (channelBits & FB_DEPTH_BIT) aspect |= GL_DEPTH_BUFFER_BIT; + if (channelBits & FB_STENCIL_BIT) + aspect |= GL_STENCIL_BUFFER_BIT; } renderManager_.CopyFramebuffer(src->framebuffer, GLRect2D{ srcX, srcY, width, height }, dst->framebuffer, GLOffset2D{ dstX, dstY }, aspect); } @@ -1589,30 +1591,34 @@ void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int s bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *fbdst, int dstX1, int dstY1, int dstX2, int dstY2, int channels, FBBlitFilter linearFilter) { OpenGLFramebuffer *src = (OpenGLFramebuffer *)fbsrc; OpenGLFramebuffer *dst = (OpenGLFramebuffer *)fbdst; - GLuint bits = 0; + GLuint aspect = 0; if (channels & FB_COLOR_BIT) - bits |= GL_COLOR_BUFFER_BIT; + aspect |= GL_COLOR_BUFFER_BIT; if (channels & FB_DEPTH_BIT) - bits |= GL_DEPTH_BUFFER_BIT; + aspect |= GL_DEPTH_BUFFER_BIT; if (channels & FB_STENCIL_BIT) - bits |= GL_STENCIL_BUFFER_BIT; + aspect |= GL_STENCIL_BUFFER_BIT; + + renderManager_.BlitFramebuffer(src->framebuffer, GLRect2D{ srcX1, srcY1, srcX2 - srcX1, srcY2 - srcY1 }, dst->framebuffer, GLRect2D{ dstX1, dstY1, dstX2 - dstX1, dstY2 - dstY1 }, aspect, linearFilter == FB_BLIT_LINEAR); + /* // Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing // in ES 2.0 that actually separate them anyway of course, so doesn't matter. fbo_bind_fb_target(false, dst->handle); fbo_bind_fb_target(true, src->handle); if (gl_extensions.GLES3 || gl_extensions.ARB_framebuffer_object) { - glBlitFramebuffer(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, bits, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); + glBlitFramebuffer(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, aspect, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); CHECK_GL_ERROR_IF_DEBUG(); #if defined(USING_GLES2) && defined(__ANDROID__) // We only support this extension on Android, it's not even available on PC. return true; } else if (gl_extensions.NV_framebuffer_blit) { - glBlitFramebufferNV(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, bits, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); + glBlitFramebufferNV(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, aspect, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); CHECK_GL_ERROR_IF_DEBUG(); #endif // defined(USING_GLES2) && defined(__ANDROID__) return true; } else { return false; - } + }*/ + return true; } void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int color) { From 46e1fbb78856c8304fbf3d4d820134462959507e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 19 Nov 2017 18:22:46 +0100 Subject: [PATCH 11/96] Initial texture work. Bugfix indexed drawing --- GPU/Common/TextureCacheCommon.cpp | 2 +- GPU/GLES/DrawEngineGLES.cpp | 17 ++-- GPU/GLES/FramebufferManagerGLES.cpp | 109 +++++++++---------------- GPU/GLES/GPU_GLES.cpp | 3 - GPU/GLES/TextureCacheGLES.cpp | 111 +++++++------------------- GPU/GLES/TextureCacheGLES.h | 7 +- ext/native/thin3d/GLRenderManager.cpp | 4 +- ext/native/thin3d/GLRenderManager.h | 12 +-- ext/native/thin3d/thin3d_gl.cpp | 3 +- 9 files changed, 91 insertions(+), 177 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index bcfa89ed31..75f9a5d14c 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -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) + (((intptr_t)entry->textureName >> 8) & 15); + entry->framesUntilNextFullHash = std::min(512, entry->numFrames) + (((intptr_t)(entry->textureName) >> 12) & 15); } else { entry->framesUntilNextFullHash = entry->numFrames; } diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index d05489dd92..b7303b05dc 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -181,8 +181,8 @@ void DrawEngineGLES::InitDeviceObjects() { } for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) { - frameData_[i].pushVertex = new GLPushBuffer(render_, 1024 * 1024); - frameData_[i].pushIndex = new GLPushBuffer(render_, 512 * 1024); + frameData_[i].pushVertex = new GLPushBuffer(render_, GL_ARRAY_BUFFER, 1024 * 1024); + frameData_[i].pushIndex = new GLPushBuffer(render_, GL_ELEMENT_ARRAY_BUFFER, 512 * 1024); } int vertexSize = sizeof(TransformedVertex); @@ -695,14 +695,7 @@ rotateVBO: } 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()); - } - } - + LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, prim); GLRInputLayout *inputLayout = SetupDecFmtForDraw(program, dec_->GetDecVtxFmt()); render_->BindVertexBuffer(vertexBuffer); @@ -777,10 +770,10 @@ rotateVBO: if (drawIndexed) { vertexBufferOffset = (uint32_t)frameData.pushVertex->Push(drawBuffer, maxIndex * sizeof(TransformedVertex), &vertexBuffer); indexBufferOffset = (uint32_t)frameData.pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &indexBuffer); - render_->BindIndexBuffer(indexBuffer); render_->BindVertexBuffer(vertexBuffer); render_->BindInputLayout(softwareInputLayout_, (void *)(intptr_t)vertexBufferOffset); - render_->DrawIndexed(glprim[prim], numTrans, GL_UNSIGNED_SHORT, inds); + render_->BindIndexBuffer(indexBuffer); + render_->DrawIndexed(glprim[prim], numTrans, GL_UNSIGNED_SHORT, (void *)(intptr_t)indexBufferOffset); } else { vertexBufferOffset = (uint32_t)frameData.pushVertex->Push(drawBuffer, numTrans * sizeof(TransformedVertex), &vertexBuffer); render_->BindVertexBuffer(vertexBuffer); diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index fccf90b1f3..854a91e46c 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -347,65 +347,48 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma // 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; } } - - // 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(); + render_->TextureImage(drawPixelsTex_, 0, texWidth, height, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, convBuf); } void FramebufferManagerGLES::SetViewport2D(int x, int y, int w, int h) { @@ -979,20 +962,6 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x 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) { diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 594c980f05..8139f142ac 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -300,9 +300,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; diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index b841a32f23..43cbc96914 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -45,10 +45,6 @@ #include #endif -#ifndef GL_UNPACK_ROW_LENGTH -#define GL_UNPACK_ROW_LENGTH 0x0CF2 -#endif - TextureCacheGLES::TextureCacheGLES(Draw::DrawContext *draw) : TextureCacheCommon(draw) { timesInvalidatedAllThisFrame_ = 0; @@ -487,7 +483,7 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram 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; @@ -674,35 +670,38 @@ 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; 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); + render_->GenerateMipmap(); } 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); + render_->GenerateMipmap(); } else { maxLevel = 0; } } } else if (gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + texMaxLevel = 0; } + // TODO: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, texMaxLevel); + if (maxLevel == 0) { entry->status |= TexCacheEntry::STATUS_BAD_MIPS; } else { @@ -712,22 +711,8 @@ 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 = aniso; //(float) aniso > maxAnisotropyLevel ? maxAnisotropyLevel : (float) aniso; - if (anisotropyLevel > 1.0f) { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropyLevel); - } - } - // This will rebind it, but that's okay. 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(); } GLenum TextureCacheGLES::GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const { @@ -752,7 +737,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); @@ -770,26 +755,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; } @@ -800,7 +785,7 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r int w = gstate.getTextureWidth(level); int h = gstate.getTextureHeight(level); bool useUnpack = false; - u32 *pixelData; + uint8_t *pixelData; CHECK_GL_ERROR_IF_DEBUG(); @@ -812,10 +797,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)); @@ -825,20 +810,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) { @@ -848,8 +827,9 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r entry.SetAlphaStatus(TexCacheEntry::STATUS_ALPHA_UNKNOWN); } - if (scaleFactor > 1) - scaler.Scale(pixelData, dstFmt, w, h, scaleFactor); + // TODO: Scale's buffer management doesn't work. + //if (scaleFactor > 1) + // scaler.Scale((uint32_t *)pixelData, dstFmt, w, h, scaleFactor); if (replacer_.Enabled()) { ReplacedTextureDecodeInfo replacedInfo; @@ -866,8 +846,6 @@ 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; @@ -876,7 +854,8 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r 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. @@ -885,37 +864,9 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r 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); } } diff --git a/GPU/GLES/TextureCacheGLES.h b/GPU/GLES/TextureCacheGLES.h index 559ccf7720..dcefcfb700 100644 --- a/GPU/GLES/TextureCacheGLES.h +++ b/GPU/GLES/TextureCacheGLES.h @@ -80,8 +80,11 @@ 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; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 301418449d..167359eaeb 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -352,7 +352,7 @@ void GLRenderManager::Wipe() { steps_.clear(); } -GLPushBuffer::GLPushBuffer(GLRenderManager *render, size_t size) : render_(render), buf_(0), offset_(0), size_(size), writePtr_(nullptr) { +GLPushBuffer::GLPushBuffer(GLRenderManager *render, GLuint target, size_t size) : render_(render), target_(target), size_(size) { bool res = AddBuffer(); assert(res); } @@ -383,7 +383,7 @@ void GLPushBuffer::Unmap() { bool GLPushBuffer::AddBuffer() { BufInfo info; info.deviceMemory = new uint8_t[size_]; - info.buffer = render_->CreateBuffer(GL_ARRAY_BUFFER, size_, GL_DYNAMIC_DRAW); + info.buffer = render_->CreateBuffer(target_, size_, GL_DYNAMIC_DRAW); buf_ = buffers_.size(); buffers_.resize(buf_ + 1); buffers_[buf_] = info; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 4c08c36e05..616f932986 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -289,6 +289,7 @@ public: 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; @@ -603,7 +604,7 @@ public: }; public: - GLPushBuffer(GLRenderManager *render, size_t size); + GLPushBuffer(GLRenderManager *render, GLuint target, size_t size); ~GLPushBuffer(); void Destroy(); @@ -691,8 +692,9 @@ private: GLRenderManager *render_; std::vector buffers_; - size_t buf_; - size_t offset_; - size_t size_; - uint8_t *writePtr_; + size_t buf_ = 0; + size_t offset_ = 0; + size_t size_ = 0; + uint8_t *writePtr_ = nullptr; + GLuint target_; }; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 33a890d90d..751e839e22 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -539,7 +539,7 @@ OpenGLContext::OpenGLContext() { break; } for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) { - frameData_[i].push = new GLPushBuffer(&renderManager_, 64 * 1024); + frameData_[i].push = new GLPushBuffer(&renderManager_, GL_ARRAY_BUFFER, 64 * 1024); } } @@ -1164,7 +1164,6 @@ void OpenGLContext::DrawUP(const void *vdata, int vertexCount) { renderManager_.BindInputLayout(curPipeline_->inputLayout->inputLayout_, (void *)offset); renderManager_.Draw(curPipeline_->prim, 0, vertexCount); - renderManager_.BindVertexBuffer(nullptr); #else ApplySamplers(); renderManager_.BindInputLayout(curPipeline_->inputLayout->inputLayout_, (void *)vdata); From f3282dcfdad0c2580aa12d6bc75f173fec07a1d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 12 Dec 2017 14:07:52 +0100 Subject: [PATCH 12/96] Delete the gl name cache (might resurrect it on the GLQueueRunner side if needed later). Other cleanup and fixes. --- GPU/Common/FramebufferCommon.h | 2 +- GPU/D3D11/FramebufferManagerD3D11.h | 3 +- GPU/GLES/DrawEngineGLES.cpp | 242 +++------------------------- GPU/GLES/DrawEngineGLES.h | 47 ++---- GPU/GLES/FramebufferManagerGLES.cpp | 176 ++++++++++---------- GPU/GLES/FramebufferManagerGLES.h | 35 ++-- GPU/GLES/GPU_GLES.cpp | 5 +- GPU/GLES/StencilBufferGLES.cpp | 46 +++--- GPU/GLES/TextureCacheGLES.cpp | 10 +- ext/native/thin3d/GLQueueRunner.cpp | 9 -- ext/native/thin3d/GLQueueRunner.h | 2 + ext/native/thin3d/GLRenderManager.h | 13 +- 12 files changed, 185 insertions(+), 405 deletions(-) diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 78005a1126..8545115e86 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -206,7 +206,7 @@ public: return vfb; } } - virtual void RebindFramebuffer(); + void RebindFramebuffer(); std::vector GetFramebufferList(); void CopyDisplayToOutput(); diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index 508e995e52..1bc40e960e 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -58,8 +58,7 @@ public: void BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; - - virtual void RebindFramebuffer() override; + void RebindFramebuffer(); // TODO: Remove ID3D11Buffer *GetDynamicQuadBuffer() { diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index b7303b05dc..aacc8f7f31 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -91,7 +91,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, @@ -146,17 +146,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(); } @@ -166,20 +155,6 @@ void DrawEngineGLES::DeviceRestore() { } void DrawEngineGLES::InitDeviceObjects() { - if (bufferNameCache_.empty()) { - bufferNameCache_.resize(VERTEXCACHE_NAME_CACHE_SIZE); - glGenBuffers(VERTEXCACHE_NAME_CACHE_SIZE, &bufferNameCache_[0]); - bufferNameCacheSize_ = 0; - - if (gstate_c.Supports(GPU_SUPPORTS_VAO)) { - glGenVertexArrays(1, &sharedVao_); - } else { - sharedVao_ = 0; - } - } else { - ERROR_LOG(G3D, "Device objects already initialized!"); - } - 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, 512 * 1024); @@ -203,18 +178,6 @@ 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_); - } - } render_->DeleteInputLayout(softwareInputLayout_); } @@ -376,11 +339,11 @@ void DrawEngineGLES::DecodeVertsToPushBuffer(GLPushBuffer *push, uint32_t *bindO 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; } } @@ -420,80 +383,14 @@ 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; } } @@ -517,7 +414,6 @@ void DrawEngineGLES::DoFlush() { uint32_t indexBufferOffset = 0; if (vshader->UseHWTransform()) { - GLuint vbo = 0, ebo = 0; int vertexCount = 0; bool useElements = true; @@ -611,31 +507,28 @@ 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); + render_->BindVertexBuffer(vai->vbo); // 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(vai->prim); break; @@ -650,10 +543,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(vai->prim); @@ -843,108 +734,12 @@ rotateVBO: 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 toFree; - std::vector 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 { return decJitCache_->IsInSpace(ptr); } 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 @@ -1041,4 +836,5 @@ void DrawEngineGLES::TessellationDataTransferGLES::SendDataToShader(const float } glActiveTexture(GL_TEXTURE0); CHECK_GL_ERROR_IF_DEBUG(); + */ } diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index 2ed6046b96..9fd2a6080d 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -61,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; @@ -82,8 +82,8 @@ public: ReliableHashType hash; u32 minihash; - u32 vbo; - u32 ebo; + GLRBuffer *vbo; + GLRBuffer *ebo; // Precalculated parameter for drawRangeElements u16 numVerts; @@ -119,7 +119,6 @@ public: void SetFragmentTestCache(FragmentTestCacheGLES *testCache) { fragmentTestCache_ = testCache; } - void RestoreVAO(); void DeviceLost(); void DeviceRestore(); @@ -151,10 +150,12 @@ 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(); @@ -171,8 +172,6 @@ private: void DecodeVertsToPushBuffer(GLPushBuffer *push, uint32_t *bindOffset, GLRBuffer **buf); - GLuint AllocateBuffer(size_t sz); - void FreeBuffer(GLuint buf); void FreeVertexArray(VertexArrayInfo *vai); void MarkUnreliable(VertexArrayInfo *vai); @@ -188,23 +187,7 @@ private: DenseHashMap inputLayoutMap_; GLRInputLayout *softwareInputLayout_ = nullptr; - - // Vertex buffer objects - // Element buffer objects - struct BufferNameInfo { - BufferNameInfo() : sz(0), used(false), lastFrame(0) {} - - size_t sz; - bool used; - int lastFrame; - }; GLRenderManager *render_; - std::vector bufferNameCache_; - std::multimap freeSizedBuffers_; - std::unordered_map bufferNameInfo_; - std::vector buffersThisFrame_; - size_t bufferNameCacheSize_ = 0; - GLuint sharedVao_ = 0; // Other ShaderManagerGLES *shaderManager_ = nullptr; @@ -218,15 +201,11 @@ private: // Hardware tessellation class TessellationDataTransferGLES : public TessellationDataTransfer { private: - int data_tex[3]; + GLRTexture *data_tex[3]{}; bool isAllowTexture1D_; public: - TessellationDataTransferGLES(bool isAllowTexture1D) : TessellationDataTransfer(), data_tex(), isAllowTexture1D_(isAllowTexture1D) { - glGenTextures(3, (GLuint*)data_tex); - } - ~TessellationDataTransferGLES() { - glDeleteTextures(3, (GLuint*)data_tex); - } + TessellationDataTransferGLES(bool isAllowTexture1D) : TessellationDataTransfer(), isAllowTexture1D_(isAllowTexture1D) { } + ~TessellationDataTransferGLES() { } void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) override; }; }; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 854a91e46c..52c2d4390f 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -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" @@ -103,13 +102,14 @@ 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 shaders; + GLRShader *vs = render_->CreateShader(GL_VERTEX_SHADER, vs_code); + GLRShader *fs = render_->CreateShader(GL_FRAGMENT_SHADER, fs_code); + std::vector queries; + queries.push_back({ &u_draw2d_tex, "u_tex" }); + std::vector initializers; + initializers.push_back({ &u_draw2d_tex, 0 }); + draw2dprogram_ = render_->CreateProgram(shaders, {}, queries, initializers, false); CompilePostShader(); } } @@ -156,7 +156,26 @@ void FramebufferManagerGLES::CompilePostShader() { } if (!translationFailed) { - postShaderProgram_ = glsl_create_source(vshader.c_str(), fshader.c_str(), &errorString); + SetNumExtraFBOs(1); + + std::vector shaders; + shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vshader)); + shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, fshader)); + std::vector 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 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!"); } @@ -194,13 +213,6 @@ 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 { @@ -211,7 +223,7 @@ void FramebufferManagerGLES::CompilePostShader() { } void FramebufferManagerGLES::Bind2DShader() { - glsl_bind(draw2dprogram_); + render_->BindProgram(draw2dprogram_); } void FramebufferManagerGLES::BindPostShader(const PostShaderUniforms &uniforms) { @@ -219,20 +231,20 @@ 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), + render_(render), drawPixelsTex_(0), drawPixelsTexFormat_(GE_FORMAT_INVALID), convBuf_(nullptr), @@ -275,15 +287,25 @@ void FramebufferManagerGLES::SetDrawEngine(DrawEngineGLES *td) { void FramebufferManagerGLES::CreateDeviceObjects() { CompileDraw2DProgram(); + + std::vector entries; + entries.push_back({ 0, 3, GL_FLOAT, GL_FALSE, sizeof(Simple2DVertex), offsetof(Simple2DVertex, pos) }); + entries.push_back({ 0, 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_) { @@ -291,7 +313,7 @@ void FramebufferManagerGLES::DestroyDeviceObjects() { drawPixelsTex_ = 0; } if (stencilUploadProgram_) { - glsl_destroy(stencilUploadProgram_); + render_->DeleteProgram(stencilUploadProgram_); stencilUploadProgram_ = nullptr; } } @@ -329,15 +351,10 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma 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); + render_->BindTexture(0, drawPixelsTex_); + render_->SetTextureSampler(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f); + /* glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); */ drawPixelsTexFormat_ = srcPixelFormat; @@ -392,7 +409,7 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma } 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. @@ -405,7 +422,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]; @@ -424,9 +441,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); @@ -438,11 +455,9 @@ 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(); @@ -450,30 +465,23 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float 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(buffer); + render_->BindInputLayout(simple2DInputLayout_, (void *)(intptr_t)bindOffset); + render_->Draw(GL_TRIANGLE_STRIP, 0, 4); } void FramebufferManagerGLES::ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old) { @@ -514,17 +522,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(0, nullptr); gstate_c.skipDrawReason |= SKIPDRAW_BAD_FB_TEXTURE; return; } @@ -691,7 +697,6 @@ void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, } } - 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 { @@ -701,38 +706,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(); + gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE); CHECK_GL_ERROR_IF_DEBUG(); } diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index 42d675ebe9..57111e3fa2 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -51,7 +51,7 @@ struct AsyncPBO { class FramebufferManagerGLES : public FramebufferManagerCommon { public: - FramebufferManagerGLES(Draw::DrawContext *draw); + FramebufferManagerGLES(Draw::DrawContext *draw, GLRenderManager *render); ~FramebufferManagerGLES(); void SetTextureCache(TextureCacheGLES *tc); @@ -82,7 +82,6 @@ public: bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; - virtual void RebindFramebuffer() override; void DeviceRestore(Draw::DrawContext *draw); @@ -104,7 +103,6 @@ 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 @@ -121,19 +119,34 @@ private: u8 *convBuf_; u32 convBufSize_; - GLSLProgram *draw2dprogram_ = nullptr; - GLSLProgram *postShaderProgram_ = nullptr; - GLSLProgram *stencilUploadProgram_ = nullptr; - int plainColorLoc_; - int videoLoc_; - int timeLoc_; - int pixelDeltaLoc_; - int deltaLoc_; + GLRProgram *draw2dprogram_ = nullptr; + GLRProgram *postShaderProgram_ = nullptr; + + + GLRProgram *stencilUploadProgram_ = nullptr; + int u_stencilUploadTex = -1; + int u_stencilValue = -1; + int u_postShaderTex = -1; + + // Cached uniform locs + int u_draw2d_tex = -1; + + int plainColorLoc_ = -1; + int videoLoc_ = -1; + int timeLoc_ = -1; + int pixelDeltaLoc_ = -1; + int deltaLoc_ = -1; TextureCacheGLES *textureCacheGL_; ShaderManagerGLES *shaderManagerGL_; DrawEngineGLES *drawEngineGL_; + struct Simple2DVertex { + float pos[3]; + float uv[2]; + }; + GLRInputLayout *simple2DInputLayout_; + // Not used under ES currently. AsyncPBO *pixelBufObj_; //this isn't that large u8 currentPBO_; diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 8139f142ac..be88cda194 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -86,7 +86,7 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) GLRenderManager *render = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); shaderManagerGL_ = new ShaderManagerGLES(render); - framebufferManagerGL_ = new FramebufferManagerGLES(draw); + framebufferManagerGL_ = new FramebufferManagerGLES(draw, render); framebufferManager_ = framebufferManagerGL_; textureCacheGL_ = new TextureCacheGLES(draw); textureCache_ = textureCacheGL_; @@ -163,7 +163,6 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) // 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. @@ -498,7 +497,6 @@ void GPU_GLES::UpdateCmdInfo() { } void GPU_GLES::ReapplyGfxState() { - drawEngine_.RestoreVAO(); glstate.Restore(); GPUCommon::ReapplyGfxState(); } @@ -509,7 +507,6 @@ void GPU_GLES::BeginFrame() { textureCacheGL_->StartFrame(); drawEngine_.DecimateTrackedVertexArrays(); - drawEngine_.DecimateBuffers(); depalShaderCache_.Decimate(); fragmentTestCache_.Decimate(); diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 01dfcf254e..15da119330 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -126,26 +126,32 @@ 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 shaders; + shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vs_code)); + shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fs_code)); + std::vector queries; + queries.push_back({ &u_stencilUploadTex, "tex" }); + std::vector 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_); + render_->BindProgram(stencilUploadProgram_); } - - GLint u_tex = glsl_uniform_loc(stencilUploadProgram_, "tex"); - glUniform1i(u_tex, 0); } else { - glsl_bind(stencilUploadProgram_); + render_->BindProgram(stencilUploadProgram_); } shaderManagerGL_->DirtyLastShader(); DisableState(); - glstate.colorMask.set(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + render_->SetNoBlendAndMask(0x8); 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 +170,41 @@ 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); + render_->Clear(0, 0, 0, GL_STENCIL_BUFFER_BIT); + render_->SetStencil(GL_TRUE, GL_ALWAYS, GL_KEEP, GL_KEEP, GL_KEEP, 0xFF, 0xFF, 0xFF); - 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_->SetStencil(GL_TRUE, GL_ALWAYS, GL_KEEP, GL_KEEP, GL_KEEP, (i << 4) | i, 0xFF, 0xFF); + 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_->SetUniformF1(&u_stencilValue, i * (128.0f / 255.0f)); } else { - glstate.stencilMask.set(i); - glUniform1f(u_stencilValue, i * (1.0f / 255.0f)); + render_->SetStencil(GL_TRUE, GL_ALWAYS, GL_KEEP, GL_KEEP, GL_KEEP, i, 0xFF, 0xFF); + 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); + render_->SetStencil(GL_TRUE, GL_ALWAYS, GL_KEEP, GL_KEEP, GL_KEEP, 0xFF, 0xFF, 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; } diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 43cbc96914..307b208342 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -401,6 +401,7 @@ public: void Use(GLRenderManager *render, DrawEngineGLES *transformDraw) { render->BindProgram(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 }; @@ -412,11 +413,12 @@ public: } glEnableVertexAttribArray(shader_->a_position); glEnableVertexAttribArray(shader_->a_texcoord0); + */ } void Shade() { static const GLubyte indices[4] = { 0, 1, 3, 2 }; - + /* glstate.blend.force(false); glstate.colorMask.force(true, true, true, true); glstate.scissorTest.force(false); @@ -441,6 +443,7 @@ public: glDisableVertexAttribArray(shader_->a_texcoord0); glstate.Restore(); + */ } protected: @@ -473,8 +476,7 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram render_->BindTexture(3, clutTexture); 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_->SetTextureSampler(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f); shaderApply.Shade(); @@ -496,8 +498,6 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram framebufferManagerGL_->RebindFramebuffer(); SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight); - CHECK_GL_ERROR_IF_DEBUG(); - InvalidateLastTexture(); } diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 37084abb05..42b25e040f 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -99,16 +99,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { break; } - // Auto-initialize samplers. glUseProgram(program->program); - for (int i = 0; i < 4; i++) { - char temp[256]; - sprintf(temp, "Sampler%i", i); - int samplerLoc = glGetUniformLocation(program->program, temp); - if (samplerLoc != -1) { - glUniform1i(samplerLoc, i); - } - } // Query all the uniforms. for (int i = 0; i < program->queries_.size(); i++) { diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index 2d96cf0fae..f6ddf68ef9 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -318,6 +318,8 @@ private: GLuint globalVAO_; GLint curFramebuffer_ = 0; + int curFBWidth_; + int curFBHeight_; // Readback buffer. Currently we only support synchronous readback, so we only really need one. // We size it generously. diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 616f932986..19c82ee043 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -122,7 +122,7 @@ public: glDeleteBuffers(1, &buffer); } } - GLuint buffer; + GLuint buffer = 0; GLuint target_; private: }; @@ -216,6 +216,8 @@ public: 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 shaders, std::vector semantics, std::vector queries, std::vector initalizers, bool supportDualSource) { @@ -398,6 +400,15 @@ public: 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 }; From 51f467a7b3a23e63e9230346b6aa6502ba347022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 12 Dec 2017 15:04:46 +0100 Subject: [PATCH 13/96] Kill off the GL state cache --- CMakeLists.txt | 2 - GPU/GLES/DepalettizeShaderGLES.cpp | 1 - GPU/GLES/DrawEngineGLES.cpp | 8 +- GPU/GLES/DrawEngineGLES.h | 2 +- GPU/GLES/FragmentTestCacheGLES.h | 1 - GPU/GLES/FramebufferManagerGLES.cpp | 1 + GPU/GLES/GPU_GLES.cpp | 18 -- GPU/GLES/ShaderManagerGLES.cpp | 2 +- GPU/GLES/StateMappingGLES.cpp | 21 +-- GPU/GLES/StencilBufferGLES.cpp | 19 +- GPU/GLES/TextureCacheGLES.cpp | 5 +- Qt/Debugger/debugger_memorytex.cpp | 1 - ext/native/Android.mk | 1 - ext/native/gfx/GLStateCache.cpp | 52 ----- ext/native/gfx/GLStateCache.h | 283 ---------------------------- ext/native/native.vcxproj | 2 - ext/native/native.vcxproj.filters | 8 +- ext/native/thin3d/GLQueueRunner.cpp | 49 +++-- ext/native/thin3d/GLQueueRunner.h | 21 ++- ext/native/thin3d/GLRenderManager.h | 39 ++-- ext/native/thin3d/thin3d.h | 2 +- ext/native/thin3d/thin3d_gl.cpp | 9 +- 22 files changed, 110 insertions(+), 437 deletions(-) delete mode 100644 ext/native/gfx/GLStateCache.cpp delete mode 100644 ext/native/gfx/GLStateCache.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a1ae75c2d..b60032d8f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -887,8 +887,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 diff --git a/GPU/GLES/DepalettizeShaderGLES.cpp b/GPU/GLES/DepalettizeShaderGLES.cpp index 7e56ce8525..b4eff02b9d 100644 --- a/GPU/GLES/DepalettizeShaderGLES.cpp +++ b/GPU/GLES/DepalettizeShaderGLES.cpp @@ -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 diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index aacc8f7f31..e61f7df9d0 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -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" @@ -585,7 +584,7 @@ rotateVBO: gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255); } - ApplyDrawStateLate(); + ApplyDrawStateLate(false, 0); LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, prim); GLRInputLayout *inputLayout = SetupDecFmtForDraw(program, dec_->GetDecVtxFmt()); @@ -646,14 +645,11 @@ rotateVBO: prim, vertexCount, dec_->VertexType(), inds, GE_VTYPE_IDX_16BIT, dec_->GetDecVtxFmt(), maxIndex, drawBuffer, numTrans, drawIndexed, ¶ms, &result); - ApplyDrawStateLate(); + 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; diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index 9fd2a6080d..ea75d346e6 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -165,7 +165,7 @@ private: void DoFlush(); void ApplyDrawState(int prim); - void ApplyDrawStateLate(); + void ApplyDrawStateLate(bool setStencil, int stencilValue); void ResetShaderBlending(); GLRInputLayout *SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt); diff --git a/GPU/GLES/FragmentTestCacheGLES.h b/GPU/GLES/FragmentTestCacheGLES.h index 95eecb23cb..2b044614a3 100644 --- a/GPU/GLES/FragmentTestCacheGLES.h +++ b/GPU/GLES/FragmentTestCacheGLES.h @@ -19,7 +19,6 @@ #include #include "Common/CommonTypes.h" -#include "ext/native/gfx/GLStateCache.h" #include "GPU/GLES/TextureCacheGLES.h" #include "GPU/ge_constants.h" diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 52c2d4390f..1fbb7044a0 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -1058,6 +1058,7 @@ void FramebufferManagerGLES::DestroyAllFBOs() { void FramebufferManagerGLES::Resized() { FramebufferManagerCommon::Resized(); + render_->Resize(PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight); if (UpdateSize()) { DestroyAllFBOs(); } diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index be88cda194..7887292baa 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -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" @@ -160,9 +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(); textureCacheGL_->NotifyConfigChanged(); // Load shader cache. @@ -431,14 +427,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() { @@ -497,7 +485,6 @@ void GPU_GLES::UpdateCmdInfo() { } void GPU_GLES::ReapplyGfxState() { - glstate.Restore(); GPUCommon::ReapplyGfxState(); } @@ -557,9 +544,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(); @@ -768,8 +752,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) { diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 08b9072feb..22d21f50d6 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -26,6 +26,7 @@ #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" @@ -39,7 +40,6 @@ #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" diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 54acebcb1c..bb5af37045 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -32,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" @@ -169,12 +168,6 @@ void DrawEngineGLES::ApplyDrawState(int prim) { bool colorMask = gstate.isClearModeColorMask(); bool alphaMask = gstate.isClearModeAlphaMask(); renderManager->SetNoBlendAndMask((colorMask ? 7 : 0) | (alphaMask ? 8 : 0)); -#ifndef USING_GLES2 - if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) { - // Logic Ops - glstate.colorLogicOp.disable(); - } -#endif } 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. @@ -280,7 +273,8 @@ void DrawEngineGLES::ApplyDrawState(int prim) { if (gstate.isClearModeDepthMask()) { framebufferManager_->SetDepthUpdated(); } - renderManager->SetStencil(gstate.isClearModeAlphaMask() && enableStencilTest, GL_ALWAYS, GL_REPLACE, GL_REPLACE, GL_REPLACE, 0xFF, 0xFF, 0xFF); + 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 @@ -293,9 +287,8 @@ void DrawEngineGLES::ApplyDrawState(int prim) { ConvertStencilFuncState(stencilState); // Stencil Test if (stencilState.enabled) { - renderManager->SetStencil(stencilState.enabled, compareOps[stencilState.testFunc], - stencilOps[stencilState.sFail], stencilOps[stencilState.zFail], stencilOps[stencilState.zPass], - stencilState.writeMask, stencilState.testMask, stencilState.testRef); + 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 { renderManager->SetStencilDisabled(); } @@ -332,7 +325,11 @@ void DrawEngineGLES::ApplyDrawState(int prim) { } } -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()) { diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 15da119330..1cd17c8c38 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -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,7 @@ 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); - + render_->Clear(0, 0, 0, GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE); return true; } @@ -150,8 +144,6 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe DisableState(); render_->SetNoBlendAndMask(0x8); - glstate.stencilTest.enable(); - glstate.stencilOp.set(GL_REPLACE, GL_REPLACE, GL_REPLACE); bool useBlit = gstate_c.Supports(GPU_SUPPORTS_ARB_FRAMEBUFFER_BLIT | GPU_SUPPORTS_NV_FRAMEBUFFER_BLIT); @@ -178,7 +170,7 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe textureCacheGL_->ForgetLastTexture(); render_->Clear(0, 0, 0, GL_STENCIL_BUFFER_BIT); - render_->SetStencil(GL_TRUE, GL_ALWAYS, GL_KEEP, GL_KEEP, GL_KEEP, 0xFF, 0xFF, 0xFF); + render_->SetStencilFunc(GL_TRUE, GL_ALWAYS, 0xFF, 0xFF); for (int i = 1; i < values; i += i) { if (!(usedBits & i)) { @@ -186,18 +178,17 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe continue; } if (dstBuffer->format == GE_FORMAT_4444) { - render_->SetStencil(GL_TRUE, GL_ALWAYS, GL_KEEP, GL_KEEP, GL_KEEP, (i << 4) | i, 0xFF, 0xFF); + render_->SetStencilOp((i << 4) | i, GL_KEEP, GL_KEEP, GL_KEEP); render_->SetUniformF1(&u_stencilValue, i * (16.0f / 255.0f)); } else if (dstBuffer->format == GE_FORMAT_5551) { - glstate.stencilMask.set(0xFF); + render_->SetStencilOp(0xFF, GL_KEEP, GL_KEEP, GL_KEEP); render_->SetUniformF1(&u_stencilValue, i * (128.0f / 255.0f)); } else { - render_->SetStencil(GL_TRUE, GL_ALWAYS, GL_KEEP, GL_KEEP, GL_KEEP, i, 0xFF, 0xFF); + render_->SetStencilOp(i, GL_KEEP, GL_KEEP, GL_KEEP); 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); } - render_->SetStencil(GL_TRUE, GL_ALWAYS, GL_KEEP, GL_KEEP, GL_KEEP, 0xFF, 0xFF, 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); diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 307b208342..9747dad92f 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -32,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" @@ -523,6 +522,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); diff --git a/Qt/Debugger/debugger_memorytex.cpp b/Qt/Debugger/debugger_memorytex.cpp index 51af9915c5..f077daaaf7 100644 --- a/Qt/Debugger/debugger_memorytex.cpp +++ b/Qt/Debugger/debugger_memorytex.cpp @@ -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" diff --git a/ext/native/Android.mk b/ext/native/Android.mk index 5656512d81..fd0e69181f 100644 --- a/ext/native/Android.mk +++ b/ext/native/Android.mk @@ -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 \ diff --git a/ext/native/gfx/GLStateCache.cpp b/ext/native/gfx/GLStateCache.cpp deleted file mode 100644 index b4edffc1aa..0000000000 --- a/ext/native/gfx/GLStateCache.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include - -#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"); - } -} diff --git a/ext/native/gfx/GLStateCache.h b/ext/native/gfx/GLStateCache.h deleted file mode 100644 index e46133709f..0000000000 --- a/ext/native/gfx/GLStateCache.h +++ /dev/null @@ -1,283 +0,0 @@ -#pragma once - -#include -#include // 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 - 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 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 colorLogicOp; - STATE1(glLogicOp, GLenum, GL_COPY) logicOp; -#endif - - // Dither - BoolState dither; - - // Cull Face - BoolState cullFace; - STATE1(glCullFace, GLenum, GL_FRONT) cullFaceMode; - STATE1(glFrontFace, GLenum, GL_CCW) frontFace; - - // Depth Test - BoolState 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 scissorTest; - STATE4(glScissor, GLint, GLint, GLsizei, GLsizei, 0, 0, 128, 128) scissorRect; - - // Stencil Test - BoolState 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; diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index 3f520beb66..a9929e396a 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -232,7 +232,6 @@ - @@ -693,7 +692,6 @@ - diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index d452261752..7336d62265 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -302,9 +302,6 @@ gfx - - gfx - thin3d @@ -775,9 +772,6 @@ gfx - - gfx - thin3d @@ -883,4 +877,4 @@ {06c6305a-a646-485b-85b9-645a24dd6553} - + \ No newline at end of file diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 42b25e040f..cf886b25ef 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -279,13 +279,24 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glBlendColor(c.blendColor.color[0], c.blendColor.color[1], c.blendColor.color[2], c.blendColor.color[3]); break; case GLRRenderCommand::VIEWPORT: + { + float y = c.viewport.vp.y; + if (!curFramebuffer_) + y = curFBHeight_ - y - c.viewport.vp.h; + // TODO: Support FP viewports through glViewportArrays - glViewport((GLint)c.viewport.vp.x, (GLint)c.viewport.vp.y, (GLsizei)c.viewport.vp.w, (GLsizei)c.viewport.vp.h); + glViewport((GLint)c.viewport.vp.x, (GLint)y, (GLsizei)c.viewport.vp.w, (GLsizei)c.viewport.vp.h); glDepthRange(c.viewport.vp.minZ, c.viewport.vp.maxZ); break; + } case GLRRenderCommand::SCISSOR: - glScissor(c.scissor.rc.x, c.scissor.rc.y, c.scissor.rc.w, c.scissor.rc.h); + { + int y = c.scissor.rc.y; + if (!curFramebuffer_) + y = curFBHeight_ - y - c.scissor.rc.h; + glScissor(c.scissor.rc.x, y, c.scissor.rc.w, c.scissor.rc.h); break; + } case GLRRenderCommand::UNIFORM4F: { int loc = c.uniform4.loc ? *c.uniform4.loc : -1; @@ -345,24 +356,30 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } break; } - case GLRRenderCommand::STENCIL: - if (c.stencil.enabled) { + case GLRRenderCommand::STENCILFUNC: + if (c.stencilFunc.enabled) { glEnable(GL_STENCIL_TEST); - glStencilFunc(c.stencil.func, c.stencil.ref, c.stencil.compareMask); - glStencilOp(c.stencil.sFail, c.stencil.zFail, c.stencil.pass); - glStencilMask(c.stencil.writeMask); } else { glDisable(GL_STENCIL_TEST); } + glStencilFunc(c.stencilFunc.func, c.stencilFunc.ref, c.stencilFunc.compareMask); + break; + case GLRRenderCommand::STENCILOP: + glStencilOp(c.stencilOp.sFail, c.stencilOp.zFail, c.stencilOp.pass); + glStencilMask(c.stencilOp.writeMask); break; case GLRRenderCommand::BINDTEXTURE: { - GLint target = c.texture.slot; - if (target != activeTexture) { - glActiveTexture(GL_TEXTURE0 + target); - activeTexture = target; + GLint slot = c.texture.slot; + if (slot != activeTexture) { + glActiveTexture(GL_TEXTURE0 + slot); + activeTexture = slot; + } + if (c.texture.texture) { + glBindTexture(c.texture.texture->target, c.texture.texture->texture); + } else { + glBindTexture(GL_TEXTURE_2D, 0); // ? } - glBindTexture(c.texture.texture->target, c.texture.texture->texture); break; } case GLRRenderCommand::BINDPROGRAM: @@ -518,7 +535,13 @@ void GLQueueRunner::PerformReadbackImage(const GLRStep &pass) { } void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) { - + if (pass.render.framebuffer) { + curFBWidth_ = pass.render.framebuffer->width; + curFBHeight_ = pass.render.framebuffer->height; + } else { + curFBWidth_ = targetWidth_; + curFBHeight_ = targetHeight_; + } } void GLQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) { diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index f6ddf68ef9..f26d995d5e 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -28,7 +28,8 @@ class GLRInputLayout; enum class GLRRenderCommand : uint8_t { DEPTH, - STENCIL, + STENCILFUNC, + STENCILOP, BLEND, BLENDCOLOR, UNIFORM4I, @@ -77,13 +78,15 @@ struct GLRRenderData { struct { GLboolean enabled; GLenum func; - uint8_t writeMask; - uint8_t compareMask; uint8_t ref; + uint8_t compareMask; + } stencilFunc; + struct { GLenum sFail; GLenum zFail; GLenum pass; - } stencil; + uint8_t writeMask; + } stencilOp; // also write mask struct { GLenum mode; // primitive GLint buffer; @@ -299,6 +302,10 @@ public: 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; + } private: void PerformBindFramebufferAsRenderTarget(const GLRStep &pass); void PerformRenderPass(const GLRStep &pass); @@ -318,8 +325,10 @@ private: GLuint globalVAO_; GLint curFramebuffer_ = 0; - int curFBWidth_; - int curFBHeight_; + 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. diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 19c82ee043..1e20c17827 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -456,25 +456,31 @@ public: curRenderStep_->commands.push_back(data); } - void SetStencil(bool enabled, GLenum func, GLenum sFail, GLenum zFail, GLenum pass, uint8_t writeMask, uint8_t compareMask, uint8_t refValue) { + void SetStencilFunc(bool enabled, GLenum func, uint8_t refValue, uint8_t compareMask) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); - GLRRenderData data{ GLRRenderCommand::STENCIL }; - data.stencil.enabled = enabled; - data.stencil.func = func; - data.stencil.sFail = sFail; - data.stencil.zFail = zFail; - data.stencil.pass = pass; - data.stencil.writeMask = writeMask; - data.stencil.compareMask = compareMask; - data.stencil.ref = refValue; + 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::STENCIL; - data.stencil.enabled = false; + data.cmd = GLRRenderCommand::STENCILFUNC; + data.stencilFunc.enabled = false; curRenderStep_->commands.push_back(data); } @@ -546,6 +552,12 @@ public: return curFrame_; } + void Resize(int width, int height) { + targetWidth_ = width; + targetHeight_ = height; + queueRunner_.Resize(width, height); + } + private: void BeginSubmitFrame(int frame); void EndSubmitFrame(int frame); @@ -601,6 +613,9 @@ private: bool useThread_ = false; int curFrame_ = 0; + + int targetWidth_ = 0; + int targetHeight_ = 0; }; diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 6bbc0d50e8..3427773c24 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -633,7 +633,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; } diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 751e839e22..cc0fb5299c 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -11,7 +11,6 @@ #include "thin3d/thin3d.h" #include "gfx/gl_common.h" #include "gfx/gl_debug_log.h" -#include "gfx/GLStateCache.h" #include "gfx_es2/gpu_features.h" #include "thin3d/GLRenderManager.h" @@ -207,7 +206,8 @@ public: void Apply(GLRenderManager *render) { render->SetDepth(depthTestEnabled, depthWriteEnabled, depthComp); - render->SetStencil(stencilEnabled, stencilCompareOp, stencilFail, stencilZFail, stencilPass, stencilWriteMask, stencilCompareMask, stencilReference); + render->SetStencilFunc(stencilEnabled, stencilCompareOp, stencilReference, stencilCompareMask); + render->SetStencilOp(stencilWriteMask, stencilFail, stencilZFail, stencilPass); } }; @@ -345,6 +345,11 @@ public: OpenGLContext(); virtual ~OpenGLContext(); + void SetTargetSize(int w, int h) override { + DrawContext::SetTargetSize(w, h); + renderManager_.Resize(w, h); + } + const DeviceCaps &GetDeviceCaps() const override { return caps_; } From 7e3e6529630da3ad2ff562702f8aedf06302f6eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 12 Dec 2017 15:32:15 +0100 Subject: [PATCH 14/96] Fix more gl-render-manager problems --- GPU/GLES/FragmentTestCacheGLES.cpp | 6 +++--- GPU/GLES/FragmentTestCacheGLES.h | 2 +- GPU/GLES/FramebufferManagerGLES.cpp | 7 +++++-- GPU/GLES/StateMappingGLES.cpp | 2 +- ext/native/thin3d/GLQueueRunner.cpp | 6 +++++- ext/native/thin3d/GLRenderManager.h | 16 +++++++++++----- 6 files changed, 26 insertions(+), 13 deletions(-) diff --git a/GPU/GLES/FragmentTestCacheGLES.cpp b/GPU/GLES/FragmentTestCacheGLES.cpp index 0228f3896e..4098186d20 100644 --- a/GPU/GLES/FragmentTestCacheGLES.cpp +++ b/GPU/GLES/FragmentTestCacheGLES.cpp @@ -37,7 +37,7 @@ FragmentTestCacheGLES::~FragmentTestCacheGLES() { delete [] scratchpad_; } -void FragmentTestCacheGLES::BindTestTexture(GLenum unit) { +void FragmentTestCacheGLES::BindTestTexture(int slot) { if (!g_Config.bFragmentTestCache) { return; } @@ -58,7 +58,7 @@ void FragmentTestCacheGLES::BindTestTexture(GLenum unit) { // Already bound, hurray. return; } - render_->BindTexture(unit, tex); + render_->BindTexture(slot, tex); lastTexture_ = tex; return; } @@ -78,7 +78,7 @@ void FragmentTestCacheGLES::BindTestTexture(GLenum unit) { GLRTexture *tex = CreateTestTexture(funcs, refs, masks, valid); lastTexture_ = tex; - render_->BindTexture(unit, tex); + render_->BindTexture(slot, tex); FragmentTestTexture item; item.lastFrame = gpuStats.numFlips; item.texture = tex; diff --git a/GPU/GLES/FragmentTestCacheGLES.h b/GPU/GLES/FragmentTestCacheGLES.h index 2b044614a3..9b6ac1d1be 100644 --- a/GPU/GLES/FragmentTestCacheGLES.h +++ b/GPU/GLES/FragmentTestCacheGLES.h @@ -65,7 +65,7 @@ public: textureCache_ = tc; } - void BindTestTexture(GLenum unit); + void BindTestTexture(int slot); void Clear(bool deleteThem = true); void Decimate(); diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 1fbb7044a0..96e8ef5d7f 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -103,13 +103,16 @@ void FramebufferManagerGLES::CompileDraw2DProgram() { vs_code = ApplyGLSLPrelude(basic_vs, GL_VERTEX_SHADER); fs_code = ApplyGLSLPrelude(tex_fs, GL_FRAGMENT_SHADER); std::vector shaders; - GLRShader *vs = render_->CreateShader(GL_VERTEX_SHADER, vs_code); - GLRShader *fs = render_->CreateShader(GL_FRAGMENT_SHADER, fs_code); + shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vs_code)); + shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fs_code)); + std::vector queries; queries.push_back({ &u_draw2d_tex, "u_tex" }); std::vector initializers; initializers.push_back({ &u_draw2d_tex, 0 }); draw2dprogram_ = render_->CreateProgram(shaders, {}, queries, initializers, false); + for (auto shader : shaders) + render_->DeleteShader(shader); CompilePostShader(); } } diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index bb5af37045..11b7e79576 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -347,7 +347,7 @@ void DrawEngineGLES::ApplyDrawStateLate(bool setStencil, int stencilValue) { // Apply last, once we know the alpha params of the texture. if (gstate.isAlphaTestEnabled() || gstate.isColorTestEnabled()) { - fragmentTestCache_->BindTestTexture(GL_TEXTURE2); + fragmentTestCache_->BindTestTexture(2); } } } diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index cf886b25ef..0f738f2ded 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -53,7 +53,9 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { { GLRProgram *program = step.create_program.program; program->program = glCreateProgram(); + _assert_msg_(G3D, step.create_program.num_shaders > 0, "Can't create a program with zero shaders"); for (int i = 0; i < step.create_program.num_shaders; i++) { + _dbg_assert_msg_(G3D, step.create_program.shaders[i]->shader, "Can't create a program with a null shader"); glAttachShader(program->program, step.create_program.shaders[i]->shader); } @@ -141,7 +143,9 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { glDeleteShader(shader); shader = 0; ILOG("%s Shader compile error:\n%s", step.create_shader.stage == GL_FRAGMENT_SHADER ? "Fragment" : "Vertex", infoLog); + step.create_shader.shader->valid = false; } + step.create_shader.shader->valid = true; break; } case GLRInitStepType::CREATE_INPUT_LAYOUT: @@ -359,10 +363,10 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { case GLRRenderCommand::STENCILFUNC: if (c.stencilFunc.enabled) { glEnable(GL_STENCIL_TEST); + glStencilFunc(c.stencilFunc.func, c.stencilFunc.ref, c.stencilFunc.compareMask); } else { glDisable(GL_STENCIL_TEST); } - glStencilFunc(c.stencilFunc.func, c.stencilFunc.ref, c.stencilFunc.compareMask); break; case GLRRenderCommand::STENCILOP: glStencilOp(c.stencilOp.sFail, c.stencilOp.zFail, c.stencilOp.pass); diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 1e20c17827..b014302f52 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -52,6 +52,7 @@ public: } } GLuint shader = 0; + bool valid = false; }; class GLRProgram { @@ -116,7 +117,7 @@ public: class GLRBuffer { public: - GLRBuffer(GLuint target) : target_(target) {} + GLRBuffer(GLuint target, size_t size) : target_(target), size_((int)size) {} ~GLRBuffer() { if (buffer) { glDeleteBuffers(1, &buffer); @@ -124,6 +125,7 @@ public: } GLuint buffer = 0; GLuint target_; + int size_; private: }; @@ -192,7 +194,7 @@ public: GLRBuffer *CreateBuffer(GLuint target, size_t size, GLuint usage) { GLRInitStep step{ GLRInitStepType::CREATE_BUFFER }; - step.create_buffer.buffer = new GLRBuffer(target); + step.create_buffer.buffer = new GLRBuffer(target, size); step.create_buffer.size = (int)size; step.create_buffer.usage = usage; initSteps_.push_back(step); @@ -227,6 +229,7 @@ public: step.create_program.program->semantics_ = semantics; step.create_program.program->queries_ = queries; step.create_program.program->initialize_ = initalizers; + _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]; } @@ -283,6 +286,8 @@ public: // 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 = offset; step.buffer_subdata.size = size; @@ -309,6 +314,7 @@ public: 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); @@ -662,12 +668,12 @@ public: // This will later allow for handling overflow correctly. size_t Allocate(size_t numBytes, GLRBuffer **vkbuf) { size_t out = offset_; - offset_ += (numBytes + 3) & ~3; // Round up to 4 bytes. - - if (offset_ >= size_) { + 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; From 062608ad7822ff690d706ddac1bc01b6b4beb4cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 13 Dec 2017 01:05:33 +0100 Subject: [PATCH 15/96] Start moving the framebuffer stuff over to the render manager --- GPU/GLES/TextureCacheGLES.cpp | 51 +--- android/jni/app-android.cpp | 7 +- ext/native/thin3d/GLQueueRunner.cpp | 359 +++++++++++++++++++++- ext/native/thin3d/GLQueueRunner.h | 18 ++ ext/native/thin3d/GLRenderManager.cpp | 18 +- ext/native/thin3d/GLRenderManager.h | 46 ++- ext/native/thin3d/thin3d_gl.cpp | 419 +------------------------- 7 files changed, 443 insertions(+), 475 deletions(-) diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 9747dad92f..ddbc29b4bf 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -121,35 +121,34 @@ 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 (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 || entry.magFilt != magFilt || entry.sClamp != sClamp || entry.tClamp != tClamp) { @@ -636,29 +635,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag // 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 @@ -703,8 +680,6 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag texMaxLevel = 0; } - // TODO: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, texMaxLevel); - if (maxLevel == 0) { entry->status |= TexCacheEntry::STATUS_BAD_MIPS; } else { diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 05306c22be..48fe18013c 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -486,12 +486,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, 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); + // Shouldn't really get here. } std::lock_guard guard(frameCommandLock); diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 0f738f2ded..7c8f61d3c5 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -8,6 +8,12 @@ #define TEXCACHE_NAME_CACHE_SIZE 16 +// Workaround for Retroarch. Simply declare +// extern GLuint g_defaultFBO; +// and set is as appropriate. Can adjust the variables in ext/native/base/display.h as +// appropriate. +GLuint g_defaultFBO = 0; + void GLQueueRunner::CreateDeviceObjects() { glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyLevel_); glGenVertexArrays(1, &globalVAO_); @@ -156,7 +162,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } case GLRInitStepType::CREATE_FRAMEBUFFER: { - // TODO + InitCreateFramebuffer(step); break; } case GLRInitStepType::TEXTURE_SUBDATA: @@ -180,6 +186,114 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } } +void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) { + GLRFramebuffer *fbo = step.create_framebuffer.framebuffer; + +#ifndef USING_GLES2 + if (!gl_extensions.ARB_framebuffer_object && gl_extensions.EXT_framebuffer_object) { + fbo_ext_create(step); + } else if (!gl_extensions.ARB_framebuffer_object) { + return; + } + // If GLES2, we have basic FBO support and can just proceed. +#endif + CHECK_GL_ERROR_IF_DEBUG(); + + // Color texture is same everywhere + glGenFramebuffers(1, &fbo->handle); + glGenTextures(1, &fbo->color_texture); + + // Create the surfaces. + glBindTexture(GL_TEXTURE_2D, fbo->color_texture); + 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_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + 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_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (gl_extensions.IsGLES) { + if (gl_extensions.OES_packed_depth_stencil) { + ILOG("Creating %i x %i FBO using DEPTH24_STENCIL8", fbo->width, fbo->height); + // Standard method + fbo->stencil_buffer = 0; + fbo->z_buffer = 0; + // 24-bit Z, 8-bit stencil combined + glGenRenderbuffers(1, &fbo->z_stencil_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, fbo->width, fbo->height); + + // Bind it all together + glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); + } else { + ILOG("Creating %i x %i FBO using separate stencil", fbo->width, fbo->height); + // TEGRA + fbo->z_stencil_buffer = 0; + // 16/24-bit Z, separate 8-bit stencil + glGenRenderbuffers(1, &fbo->z_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_buffer); + // Don't forget to make sure fbo_standard_z_depth() matches. + glRenderbufferStorage(GL_RENDERBUFFER, gl_extensions.OES_depth24 ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16, fbo->width, fbo->height); + + // 8-bit stencil buffer + glGenRenderbuffers(1, &fbo->stencil_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, fbo->stencil_buffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, fbo->width, fbo->height); + + // Bind it all together + glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_buffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->stencil_buffer); + } + } else { + fbo->stencil_buffer = 0; + fbo->z_buffer = 0; + // 24-bit Z, 8-bit stencil + glGenRenderbuffers(1, &fbo->z_stencil_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, fbo->width, fbo->height); + + // Bind it all together + glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + switch (status) { + case GL_FRAMEBUFFER_COMPLETE: + // ILOG("Framebuffer verified complete."); + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + ELOG("GL_FRAMEBUFFER_UNSUPPORTED"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + ELOG("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT "); + break; + default: + FLOG("Other framebuffer error: %i", status); + break; + } + + // Unbind state we don't need + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + CHECK_GL_ERROR_IF_DEBUG(); + + currentDrawHandle_ = fbo->handle; + currentReadHandle_ = fbo->handle; +} + void GLQueueRunner::RunSteps(const std::vector &steps) { for (int i = 0; i < steps.size(); i++) { const GLRStep &step = *steps[i]; @@ -213,6 +327,25 @@ void GLQueueRunner::LogSteps(const std::vector &steps) { void GLQueueRunner::PerformBlit(const GLRStep &step) { + /* + // Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing + // in ES 2.0 that actually separate them anyway of course, so doesn't matter. + fbo_bind_fb_target(false, dst->handle); + fbo_bind_fb_target(true, src->handle); + if (gl_extensions.GLES3 || gl_extensions.ARB_framebuffer_object) { + glBlitFramebuffer(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, aspect, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); + CHECK_GL_ERROR_IF_DEBUG(); + #if defined(USING_GLES2) && defined(__ANDROID__) // We only support this extension on Android, it's not even available on PC. + return true; + } else if (gl_extensions.NV_framebuffer_blit) { + glBlitFramebufferNV(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, aspect, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); + CHECK_GL_ERROR_IF_DEBUG(); + #endif // defined(USING_GLES2) && defined(__ANDROID__) + return true; + } else { + return false; + }*/ + } void GLQueueRunner::PerformRenderPass(const GLRStep &step) { @@ -236,6 +369,9 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { int attrMask = 0; + // TODO: We can implement state-filtering locally in this function, to mimic the old gl state tracker, + // to avoid redundant calls. Might be worth it? + auto &commands = step.commands; for (const auto &c : commands) { switch (c.cmd) { @@ -445,6 +581,13 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, c.textureSampler.anisotropy); } break; + case GLRRenderCommand::TEXTURELOD: + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, c.textureLod.minLod); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, c.textureLod.maxLod); +#ifndef USING_GLES2 + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, c.textureLod.lodBias); +#endif + break; case GLRRenderCommand::RASTER: if (c.raster.cullEnable) { glEnable(GL_CULL_FACE); @@ -498,13 +641,16 @@ void GLQueueRunner::PerformCopy(const GLRStep &step) { switch (step.copy.aspectMask) { case GL_COLOR_BUFFER_BIT: - srcTex = src->color.texture; - dstTex = dst->color.texture; + srcTex = src->color_texture; + dstTex = dst->color_texture; break; case GL_DEPTH_BUFFER_BIT: + _assert_msg_(G3D, false, "Depth copies not yet supported - soon"); target = GL_RENDERBUFFER; + /* srcTex = src->depth.texture; dstTex = src->depth.texture; + */ break; } #if defined(USING_GLES2) @@ -546,6 +692,59 @@ void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) { curFBWidth_ = targetWidth_; curFBHeight_ = targetHeight_; } + +#if 0 + + CHECK_GL_ERROR_IF_DEBUG(); + curFB_ = (OpenGLFramebuffer *)fbo; + if (fbo) { + OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; + // Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing + // in ES 2.0 that actually separate them anyway of course, so doesn't matter. + fbo_bind_fb_target(false, fb->handle); + // Always restore viewport after render target binding. Works around driver bugs. + glstate.viewport.restore(); + } else { + fbo_unbind(); + } + int clearFlags = 0; + if (rp.color == RPAction::CLEAR) { + float fc[4]{}; + if (rp.clearColor) { + Uint8x4ToFloat4(fc, rp.clearColor); + } + glClearColor(fc[0], fc[1], fc[2], fc[3]); + clearFlags |= GL_COLOR_BUFFER_BIT; + glstate.colorMask.force(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + if (rp.depth == RPAction::CLEAR) { +#ifdef USING_GLES2 + glClearDepthf(rp.clearDepth); +#else + glClearDepth(rp.clearDepth); +#endif + glClearStencil(rp.clearStencil); + clearFlags |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; + glstate.depthWrite.force(GL_TRUE); + glstate.stencilFunc.force(GL_ALWAYS, 0, 0); + glstate.stencilMask.force(0xFF); + } + if (clearFlags) { + glstate.scissorTest.force(false); + glClear(clearFlags); + glstate.scissorTest.restore(); + } + if (rp.color == RPAction::CLEAR) { + glstate.colorMask.restore(); + } + if (rp.depth == RPAction::CLEAR) { + glstate.depthWrite.restore(); + glstate.stencilFunc.restore(); + glstate.stencilMask.restore(); + } + CHECK_GL_ERROR_IF_DEBUG(); +#endif + } void GLQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) { @@ -562,3 +761,157 @@ GLuint GLQueueRunner::AllocTextureName() { return name; } + +// On PC, we always use GL_DEPTH24_STENCIL8. +// On Android, we try to use what's available. + +#ifndef USING_GLES2 +void GLQueueRunner::fbo_ext_create(const GLRInitStep &step) { + GLRFramebuffer *fbo = step.create_framebuffer.framebuffer; + + // Color texture is same everywhere + glGenFramebuffersEXT(1, &fbo->handle); + glGenTextures(1, &fbo->color_texture); + + // Create the surfaces. + glBindTexture(GL_TEXTURE_2D, fbo->color_texture); + 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_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + 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_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + fbo->stencil_buffer = 0; + fbo->z_buffer = 0; + // 24-bit Z, 8-bit stencil + glGenRenderbuffersEXT(1, &fbo->z_stencil_buffer); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT, fbo->width, fbo->height); + //glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8, width, height); + + // Bind it all together + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->handle); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fbo->color_texture, 0); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); + + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + switch (status) { + case GL_FRAMEBUFFER_COMPLETE_EXT: + // ILOG("Framebuffer verified complete."); + break; + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + ELOG("GL_FRAMEBUFFER_UNSUPPORTED"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: + ELOG("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT "); + break; + default: + FLOG("Other framebuffer error: %i", status); + break; + } + // Unbind state we don't need + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + glBindTexture(GL_TEXTURE_2D, 0); + + currentDrawHandle_ = fbo->handle; + currentReadHandle_ = fbo->handle; +} +#endif + +GLenum GLQueueRunner::fbo_get_fb_target(bool read, GLuint **cached) { + bool supportsBlit = gl_extensions.ARB_framebuffer_object; + if (gl_extensions.IsGLES) { + supportsBlit = (gl_extensions.GLES3 || gl_extensions.NV_framebuffer_blit); + } + + // Note: GL_FRAMEBUFFER_EXT and GL_FRAMEBUFFER have the same value, same with _NV. + if (supportsBlit) { + if (read) { + *cached = ¤tReadHandle_; + return GL_READ_FRAMEBUFFER; + } else { + *cached = ¤tDrawHandle_; + return GL_DRAW_FRAMEBUFFER; + } + } else { + *cached = ¤tDrawHandle_; + return GL_FRAMEBUFFER; + } +} + +void GLQueueRunner::fbo_bind_fb_target(bool read, GLuint name) { + GLuint *cached; + GLenum target = fbo_get_fb_target(read, &cached); + if (*cached != name) { + if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { + glBindFramebuffer(target, name); + } else { +#ifndef USING_GLES2 + glBindFramebufferEXT(target, name); +#endif + } + *cached = name; + } +} + +void GLQueueRunner::fbo_unbind() { +#ifndef USING_GLES2 + if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { + glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); + } else if (gl_extensions.EXT_framebuffer_object) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_defaultFBO); + } +#else + glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); +#endif + +#ifdef IOS + bindDefaultFBO(); +#endif + + currentDrawHandle_ = 0; + currentReadHandle_ = 0; +} + +GLRFramebuffer::~GLRFramebuffer() { + if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { + if (handle) { + glBindFramebuffer(GL_FRAMEBUFFER, handle); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); + glDeleteFramebuffers(1, &handle); + } + if (z_stencil_buffer) + glDeleteRenderbuffers(1, &z_stencil_buffer); + if (z_buffer) + glDeleteRenderbuffers(1, &z_buffer); + if (stencil_buffer) + glDeleteRenderbuffers(1, &stencil_buffer); + } else if (gl_extensions.EXT_framebuffer_object) { +#ifndef USING_GLES2 + if (handle) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, handle); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER_EXT, 0); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_defaultFBO); + glDeleteFramebuffersEXT(1, &handle); + } + if (z_stencil_buffer) + glDeleteRenderbuffers(1, &z_stencil_buffer); + if (z_buffer) + glDeleteRenderbuffers(1, &z_buffer); + if (stencil_buffer) + glDeleteRenderbuffers(1, &stencil_buffer); +#endif + } + + glDeleteTextures(1, &color_texture); +} \ No newline at end of file diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index f26d995d5e..e01a80075a 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -36,6 +36,7 @@ enum class GLRRenderCommand : uint8_t { UNIFORM4F, UNIFORMMATRIX, TEXTURESAMPLER, + TEXTURELOD, VIEWPORT, SCISSOR, RASTER, @@ -143,6 +144,11 @@ struct GLRRenderData { GLenum minFilter; // also includes mip. GL... float anisotropy; } textureSampler; + struct { + float minLod; + float maxLod; + float lodBias; + } textureLod; struct { GLRViewport vp; } viewport; @@ -307,6 +313,8 @@ public: targetHeight_ = height; } private: + void InitCreateFramebuffer(const GLRInitStep &step); + void PerformBindFramebufferAsRenderTarget(const GLRStep &pass); void PerformRenderPass(const GLRStep &pass); void PerformCopy(const GLRStep &pass); @@ -322,6 +330,12 @@ private: void ResizeReadbackBuffer(size_t requiredSize); + 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(); + + GLuint globalVAO_; GLint curFramebuffer_ = 0; @@ -337,6 +351,10 @@ private: float maxAnisotropyLevel_; + // Framebuffer state? + GLuint currentDrawHandle_ = 0; + GLuint currentReadHandle_ = 0; + GLuint AllocTextureName(); // Texture name cache. Ripped straight from TextureCacheGLES. std::vector nameCache_; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 167359eaeb..e00f11f7b3 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -1,6 +1,7 @@ #include #include "GLRenderManager.h" +#include "gfx_es2/gpu_features.h" #include "thread/threadutil.h" #include "base/logging.h" @@ -10,10 +11,6 @@ #define VLOG(...) #endif -void GLCreateImage(GLRImage &img, int width, int height, GLuint format, bool color) { - -} - void GLDeleter::Perform() { for (auto shader : shaders) { delete shader; @@ -21,7 +18,18 @@ void GLDeleter::Perform() { for (auto program : programs) { delete program; } - // .. + for (auto buffer : buffers) { + delete buffer; + } + for (auto texture : textures) { + delete texture; + } + for (auto inputLayout : inputLayouts) { + delete inputLayout; + } + for (auto framebuffer : framebuffers) { + delete framebuffer; + } } GLRenderManager::GLRenderManager() { diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index b014302f52..34bc3d238b 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -11,13 +11,6 @@ #include "Common/Log.h" #include "GLQueueRunner.h" -struct GLRImage { - GLuint texture; - GLuint format; -}; - -void GLCreateImage(GLRImage &img, int width, int height, GLint format, bool color); - class GLRInputLayout; class GLRFramebuffer { @@ -26,18 +19,21 @@ public: : width(_width), height(_height), z_stencil_(z_stencil) { } - ~GLRFramebuffer() { - glDeleteTextures(1, &color.texture); - glDeleteRenderbuffers(1, &depth.texture); - } + ~GLRFramebuffer(); int numShadows = 1; // TODO: Support this. + GLuint handle = 0; + GLuint color_texture = 0; + 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; + GLuint framebuf = 0; - GLRImage color{}; - GLRImage depth{}; - int width = 0; - int height = 0; bool z_stencil_; }; @@ -144,6 +140,13 @@ public: 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 shaders; @@ -151,6 +154,7 @@ public: std::vector buffers; std::vector textures; std::vector inputLayouts; + std::vector framebuffers; }; class GLRInputLayout { @@ -272,6 +276,9 @@ public: void DeleteInputLayout(GLRInputLayout *inputLayout) { deleter_.inputLayouts.push_back(inputLayout); } + void DeleteFramebuffer(GLRFramebuffer *framebuffer) { + deleter_.framebuffers.push_back(framebuffer); + } void BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); void BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit, int attachment); @@ -519,6 +526,15 @@ public: 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) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData data{ GLRRenderCommand::CLEAR }; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index cc0fb5299c..ee6b366516 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -21,12 +21,6 @@ extern void bindDefaultFBO(); // #define DEBUG_READ_PIXELS 1 -// Workaround for Retroarch. Simply declare -// extern GLuint g_defaultFBO; -// and set is as appropriate. Can adjust the variables in ext/native/base/display.h as -// appropriate. -GLuint g_defaultFBO = 0; - namespace Draw { static const unsigned short compToGL[] = { @@ -483,10 +477,6 @@ public: void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override {} private: - OpenGLFramebuffer *fbo_ext_create(const FramebufferDesc &desc); - void fbo_bind_fb_target(bool read, GLuint name); - GLenum fbo_get_fb_target(bool read, GLuint **cached); - void fbo_unbind(); void ApplySamplers(); GLRenderManager renderManager_; @@ -504,10 +494,6 @@ private: int curIBufferOffset_ = 0; OpenGLFramebuffer *curFB_; - // Framebuffer state - GLuint currentDrawHandle_ = 0; - GLuint currentReadHandle_ = 0; - // Frames in flight is not such a strict concept as with Vulkan until we start using glBufferStorage and fences. // But might as well have the structure ready, and can't hurt to rotate buffers. struct FrameData { @@ -652,16 +638,6 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : } mipLevels_ = desc.generateMips ? desc.mipLevels : level; - /* -#ifdef USING_GLES2 - if (gl_extensions.GLES3) { - glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, mipLevels_ - 1); - } -#else - glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, mipLevels_ - 1); -#endif -*/ - if ((int)desc.initData.size() < desc.mipLevels && desc.generateMips) { ILOG("Generating mipmaps"); AutoGenMipmaps(); @@ -687,19 +663,14 @@ void OpenGLTexture::AutoGenMipmaps() { class OpenGLFramebuffer : public Framebuffer { public: - OpenGLFramebuffer() {} - ~OpenGLFramebuffer(); + OpenGLFramebuffer(GLRenderManager *render) : render_(render) {} + ~OpenGLFramebuffer() { + render_->DeleteFramebuffer(framebuffer); + } + GLRenderManager *render_; GLRFramebuffer *framebuffer; - GLuint handle = 0; - GLuint color_texture = 0; - 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; FBColorDepth colorDepth; }; @@ -852,6 +823,7 @@ static void LogReadPixelsError(GLenum error) { bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels, int pixelStride) { OpenGLFramebuffer *fb = (OpenGLFramebuffer *)src; + /* fbo_bind_fb_target(true, fb ? fb->handle : 0); // Reads from the "bound for read" framebuffer. @@ -883,6 +855,7 @@ bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBit glPixelStorei(GL_PACK_ROW_LENGTH, 0); } CHECK_GL_ERROR_IF_DEBUG(); + */ return true; } @@ -1245,335 +1218,20 @@ void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) { inputLayout_ = render_->CreateInputLayout(entries); } -// On PC, we always use GL_DEPTH24_STENCIL8. -// On Android, we try to use what's available. - -#ifndef USING_GLES2 -OpenGLFramebuffer *OpenGLContext::fbo_ext_create(const FramebufferDesc &desc) { - OpenGLFramebuffer *fbo = new OpenGLFramebuffer(); - fbo->width = desc.width; - fbo->height = desc.height; - fbo->colorDepth = desc.colorDepth; - - // Color texture is same everywhere - glGenFramebuffersEXT(1, &fbo->handle); - glGenTextures(1, &fbo->color_texture); - - // Create the surfaces. - glBindTexture(GL_TEXTURE_2D, fbo->color_texture); - 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_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // TODO: We could opt to only create 16-bit render targets on slow devices. For later. - switch (fbo->colorDepth) { - case FBO_8888: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - break; - case FBO_4444: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, NULL); - break; - case FBO_5551: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, NULL); - break; - case FBO_565: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fbo->width, fbo->height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); - break; - } - - 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_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - fbo->stencil_buffer = 0; - fbo->z_buffer = 0; - // 24-bit Z, 8-bit stencil - glGenRenderbuffersEXT(1, &fbo->z_stencil_buffer); - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); - glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT, fbo->width, fbo->height); - //glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8, width, height); - - // Bind it all together - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->handle); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fbo->color_texture, 0); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); - - GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - switch (status) { - case GL_FRAMEBUFFER_COMPLETE_EXT: - // ILOG("Framebuffer verified complete."); - break; - case GL_FRAMEBUFFER_UNSUPPORTED_EXT: - ELOG("GL_FRAMEBUFFER_UNSUPPORTED"); - break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: - ELOG("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT "); - break; - default: - FLOG("Other framebuffer error: %i", status); - break; - } - // Unbind state we don't need - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); - glBindTexture(GL_TEXTURE_2D, 0); - - currentDrawHandle_ = fbo->handle; - currentReadHandle_ = fbo->handle; - return fbo; -} -#endif - Framebuffer *OpenGLContext::CreateFramebuffer(const FramebufferDesc &desc) { CheckGLExtensions(); - OpenGLFramebuffer *fbo = new OpenGLFramebuffer(); - // fbo->framebuffer = renderManager_.CreateFramebuffer() - -#ifndef USING_GLES2 - if (!gl_extensions.ARB_framebuffer_object && gl_extensions.EXT_framebuffer_object) { - return fbo_ext_create(desc); - } else if (!gl_extensions.ARB_framebuffer_object) { - return nullptr; - } - // If GLES2, we have basic FBO support and can just proceed. -#endif - CHECK_GL_ERROR_IF_DEBUG(); - - fbo->width = desc.width; - fbo->height = desc.height; - fbo->colorDepth = desc.colorDepth; - - // Color texture is same everywhere - glGenFramebuffers(1, &fbo->handle); - glGenTextures(1, &fbo->color_texture); - - // Create the surfaces. - glBindTexture(GL_TEXTURE_2D, fbo->color_texture); - 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_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // TODO: We could opt to only create 16-bit render targets on slow devices. For later. - switch (fbo->colorDepth) { - case FBO_8888: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - break; - case FBO_4444: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, NULL); - break; - case FBO_5551: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, NULL); - break; - case FBO_565: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fbo->width, fbo->height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); - break; - } - - 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_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - if (gl_extensions.IsGLES) { - if (gl_extensions.OES_packed_depth_stencil) { - ILOG("Creating %i x %i FBO using DEPTH24_STENCIL8", fbo->width, fbo->height); - // Standard method - fbo->stencil_buffer = 0; - fbo->z_buffer = 0; - // 24-bit Z, 8-bit stencil combined - glGenRenderbuffers(1, &fbo->z_stencil_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, fbo->width, fbo->height); - - // Bind it all together - glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); - } else { - ILOG("Creating %i x %i FBO using separate stencil", fbo->width, fbo->height); - // TEGRA - fbo->z_stencil_buffer = 0; - // 16/24-bit Z, separate 8-bit stencil - glGenRenderbuffers(1, &fbo->z_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_buffer); - // Don't forget to make sure fbo_standard_z_depth() matches. - glRenderbufferStorage(GL_RENDERBUFFER, gl_extensions.OES_depth24 ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16, fbo->width, fbo->height); - - // 8-bit stencil buffer - glGenRenderbuffers(1, &fbo->stencil_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, fbo->stencil_buffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, fbo->width, fbo->height); - - // Bind it all together - glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_buffer); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->stencil_buffer); - } - } else { - fbo->stencil_buffer = 0; - fbo->z_buffer = 0; - // 24-bit Z, 8-bit stencil - glGenRenderbuffers(1, &fbo->z_stencil_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, fbo->width, fbo->height); - - // Bind it all together - glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); - } - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - switch (status) { - case GL_FRAMEBUFFER_COMPLETE: - // ILOG("Framebuffer verified complete."); - break; - case GL_FRAMEBUFFER_UNSUPPORTED: - ELOG("GL_FRAMEBUFFER_UNSUPPORTED"); - break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - ELOG("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT "); - break; - default: - FLOG("Other framebuffer error: %i", status); - break; - } - - // Unbind state we don't need - glBindRenderbuffer(GL_RENDERBUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); - CHECK_GL_ERROR_IF_DEBUG(); - - currentDrawHandle_ = fbo->handle; - currentReadHandle_ = fbo->handle; + OpenGLFramebuffer *fbo = new OpenGLFramebuffer(&renderManager_); + fbo->framebuffer = renderManager_.CreateFramebuffer(desc.width, desc.height, desc.z_stencil); return fbo; } -GLenum OpenGLContext::fbo_get_fb_target(bool read, GLuint **cached) { - bool supportsBlit = gl_extensions.ARB_framebuffer_object; - if (gl_extensions.IsGLES) { - supportsBlit = (gl_extensions.GLES3 || gl_extensions.NV_framebuffer_blit); - } - - // Note: GL_FRAMEBUFFER_EXT and GL_FRAMEBUFFER have the same value, same with _NV. - if (supportsBlit) { - if (read) { - *cached = ¤tReadHandle_; - return GL_READ_FRAMEBUFFER; - } else { - *cached = ¤tDrawHandle_; - return GL_DRAW_FRAMEBUFFER; - } - } else { - *cached = ¤tDrawHandle_; - return GL_FRAMEBUFFER; - } -} - -void OpenGLContext::fbo_bind_fb_target(bool read, GLuint name) { - GLuint *cached; - GLenum target = fbo_get_fb_target(read, &cached); - if (*cached != name) { - if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { - glBindFramebuffer(target, name); - } else { -#ifndef USING_GLES2 - glBindFramebufferEXT(target, name); -#endif - } - *cached = name; - } -} - -void OpenGLContext::fbo_unbind() { -#ifndef USING_GLES2 - if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { - glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); - } else if (gl_extensions.EXT_framebuffer_object) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_defaultFBO); - } -#else - glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); -#endif - -#ifdef IOS - bindDefaultFBO(); -#endif - - currentDrawHandle_ = 0; - currentReadHandle_ = 0; -} - void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; GLRRenderPassAction color = (GLRRenderPassAction)rp.color; GLRRenderPassAction depth = (GLRRenderPassAction)rp.depth; renderManager_.BindFramebufferAsRenderTarget(fb ? fb->framebuffer : nullptr, color, depth, rp.clearColor, rp.clearDepth, rp.clearStencil); - -#if 0 - - CHECK_GL_ERROR_IF_DEBUG(); - curFB_ = (OpenGLFramebuffer *)fbo; - if (fbo) { - OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; - // Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing - // in ES 2.0 that actually separate them anyway of course, so doesn't matter. - fbo_bind_fb_target(false, fb->handle); - // Always restore viewport after render target binding. Works around driver bugs. - glstate.viewport.restore(); - } else { - fbo_unbind(); - } - int clearFlags = 0; - if (rp.color == RPAction::CLEAR) { - float fc[4]{}; - if (rp.clearColor) { - Uint8x4ToFloat4(fc, rp.clearColor); - } - glClearColor(fc[0], fc[1], fc[2], fc[3]); - clearFlags |= GL_COLOR_BUFFER_BIT; - glstate.colorMask.force(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - } - if (rp.depth == RPAction::CLEAR) { -#ifdef USING_GLES2 - glClearDepthf(rp.clearDepth); -#else - glClearDepth(rp.clearDepth); -#endif - clearFlags |= GL_DEPTH_BUFFER_BIT; - glstate.depthWrite.force(GL_TRUE); - } - if (rp.stencil == RPAction::CLEAR) { - glClearStencil(rp.clearStencil); - clearFlags |= GL_STENCIL_BUFFER_BIT; - glstate.stencilFunc.force(GL_ALWAYS, 0, 0); - glstate.stencilMask.force(0xFF); - } - if (clearFlags) { - glstate.scissorTest.force(false); - glClear(clearFlags); - glstate.scissorTest.restore(); - } - if (rp.color == RPAction::CLEAR) { - glstate.colorMask.restore(); - } - if (rp.depth == RPAction::CLEAR) { - glstate.depthWrite.restore(); - } - if (rp.stencil == RPAction::CLEAR) { - glstate.stencilFunc.restore(); - glstate.stencilMask.restore(); - } - CHECK_GL_ERROR_IF_DEBUG(); -#endif } void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int srcX, int srcY, int srcZ, Framebuffer *fbdst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) { @@ -1604,24 +1262,6 @@ bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, in aspect |= GL_STENCIL_BUFFER_BIT; renderManager_.BlitFramebuffer(src->framebuffer, GLRect2D{ srcX1, srcY1, srcX2 - srcX1, srcY2 - srcY1 }, dst->framebuffer, GLRect2D{ dstX1, dstY1, dstX2 - dstX1, dstY2 - dstY1 }, aspect, linearFilter == FB_BLIT_LINEAR); - /* - // Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing - // in ES 2.0 that actually separate them anyway of course, so doesn't matter. - fbo_bind_fb_target(false, dst->handle); - fbo_bind_fb_target(true, src->handle); - if (gl_extensions.GLES3 || gl_extensions.ARB_framebuffer_object) { - glBlitFramebuffer(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, aspect, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); - CHECK_GL_ERROR_IF_DEBUG(); -#if defined(USING_GLES2) && defined(__ANDROID__) // We only support this extension on Android, it's not even available on PC. - return true; - } else if (gl_extensions.NV_framebuffer_blit) { - glBlitFramebufferNV(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, aspect, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); - CHECK_GL_ERROR_IF_DEBUG(); -#endif // defined(USING_GLES2) && defined(__ANDROID__) - return true; - } else { - return false; - }*/ return true; } @@ -1632,48 +1272,11 @@ void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBCh renderManager_.BindFramebufferAsTexture(fb->framebuffer, binding, (int)channelBit, color); } -OpenGLFramebuffer::~OpenGLFramebuffer() { - CHECK_GL_ERROR_IF_DEBUG(); - if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { - if (handle) { - glBindFramebuffer(GL_FRAMEBUFFER, handle); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); - glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); - glDeleteFramebuffers(1, &handle); - } - if (z_stencil_buffer) - glDeleteRenderbuffers(1, &z_stencil_buffer); - if (z_buffer) - glDeleteRenderbuffers(1, &z_buffer); - if (stencil_buffer) - glDeleteRenderbuffers(1, &stencil_buffer); - } else if (gl_extensions.EXT_framebuffer_object) { -#ifndef USING_GLES2 - if (handle) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, handle); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER_EXT, 0); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_defaultFBO); - glDeleteFramebuffersEXT(1, &handle); - } - if (z_stencil_buffer) - glDeleteRenderbuffers(1, &z_stencil_buffer); - if (z_buffer) - glDeleteRenderbuffers(1, &z_buffer); - if (stencil_buffer) - glDeleteRenderbuffers(1, &stencil_buffer); -#endif - } - - glDeleteTextures(1, &color_texture); -} - void OpenGLContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; if (fb) { - *w = fb->width; - *h = fb->height; + *w = fb->framebuffer->width; + *h = fb->framebuffer->height; } else { *w = targetWidth_; *h = targetHeight_; From 8a1e7347b9ab83dd8dbe610c066514654c049a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 14 Dec 2017 14:53:27 +0100 Subject: [PATCH 16/96] GL render manager: Various fixes and cleanup including a buffered rendering fix, rect primitive fix --- GPU/Common/FramebufferCommon.cpp | 7 -- GPU/GLES/DepalettizeShaderGLES.cpp | 31 ------ GPU/GLES/DrawEngineGLES.cpp | 2 +- GPU/GLES/FramebufferManagerGLES.cpp | 84 +++----------- GPU/GLES/FramebufferManagerGLES.h | 2 +- ext/native/thin3d/GLQueueRunner.cpp | 151 +++++++++++++++----------- ext/native/thin3d/GLQueueRunner.h | 6 +- ext/native/thin3d/GLRenderManager.cpp | 2 - ext/native/thin3d/GLRenderManager.h | 23 +++- ext/native/thin3d/thin3d_gl.cpp | 8 +- 10 files changed, 134 insertions(+), 182 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index a54e58c61e..7425c88761 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1032,13 +1032,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); diff --git a/GPU/GLES/DepalettizeShaderGLES.cpp b/GPU/GLES/DepalettizeShaderGLES.cpp index b4eff02b9d..6ea73043a1 100644 --- a/GPU/GLES/DepalettizeShaderGLES.cpp +++ b/GPU/GLES/DepalettizeShaderGLES.cpp @@ -57,37 +57,6 @@ 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(Draw::DrawContext *draw) { render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); // Pre-build the vertex program diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index e61f7df9d0..92efcc43fb 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -656,7 +656,7 @@ rotateVBO: if (drawIndexed) { vertexBufferOffset = (uint32_t)frameData.pushVertex->Push(drawBuffer, maxIndex * sizeof(TransformedVertex), &vertexBuffer); - indexBufferOffset = (uint32_t)frameData.pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &indexBuffer); + indexBufferOffset = (uint32_t)frameData.pushIndex->Push(inds, sizeof(uint16_t) * numTrans, &indexBuffer); render_->BindVertexBuffer(vertexBuffer); render_->BindInputLayout(softwareInputLayout_, (void *)(intptr_t)vertexBufferOffset); render_->BindIndexBuffer(indexBuffer); diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 96e8ef5d7f..2f033d736c 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -81,18 +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); } @@ -293,7 +281,7 @@ void FramebufferManagerGLES::CreateDeviceObjects() { std::vector entries; entries.push_back({ 0, 3, GL_FLOAT, GL_FALSE, sizeof(Simple2DVertex), offsetof(Simple2DVertex, pos) }); - entries.push_back({ 0, 2, GL_FLOAT, GL_FALSE, sizeof(Simple2DVertex), offsetof(Simple2DVertex, uv) }); + entries.push_back({ 1, 2, GL_FLOAT, GL_FALSE, sizeof(Simple2DVertex), offsetof(Simple2DVertex, uv) }); simple2DInputLayout_ = render_->CreateInputLayout(entries); } @@ -326,7 +314,9 @@ FramebufferManagerGLES::~FramebufferManagerGLES() { if (pixelBufObj_) { for (int i = 0; i < MAX_PBO; i++) { - glDeleteBuffers(1, &pixelBufObj_[i].handle); + if (pixelBufObj_[i].buffer) { + render_->DeleteBuffer(pixelBufObj_[i].buffer); + } } delete[] pixelBufObj_; } @@ -357,9 +347,7 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma render_->BindTexture(0, drawPixelsTex_); render_->SetTextureSampler(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f); - /* - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - */ + // render_->TextureImage(drawPixelsTex_, 0, drawPixelsTexW_, drawPixelsTexH_, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); drawPixelsTexFormat_ = srcPixelFormat; } else { render_->BindTexture(0, drawPixelsTex_); @@ -463,11 +451,6 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float 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); @@ -642,7 +625,6 @@ void FramebufferManagerGLES::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) } else if (gl_extensions.IsGLES) { draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); } - 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) { @@ -695,7 +677,6 @@ 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; } } @@ -724,7 +705,6 @@ void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, } gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE); - CHECK_GL_ERROR_IF_DEBUG(); } // TODO: SSE/NEON @@ -785,7 +765,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; @@ -799,21 +778,13 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { 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; - } + pixelBufObj_ = new AsyncPBO[MAX_PBO]{}; } // Receive previously requested data from a PBO AsyncPBO &pbo = pixelBufObj_[nextPBO]; if (pbo.reading) { - glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle); + render_->BindPixelPackBuffer(pbo.buffer); #ifdef USING_GLES2 // Not on desktop GL 2.x... packed = (GLubyte *)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, pbo.size, GL_MAP_READ_BIT); @@ -875,14 +846,15 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { 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); + if (pixelBufObj_[currentPBO_].buffer) { + render_->DeleteBuffer(pixelBufObj_[currentPBO_].buffer); + } + pixelBufObj_[currentPBO_].buffer = render_->CreateBuffer(GL_PIXEL_PACK_BUFFER, bufSize, GL_DYNAMIC_READ); pixelBufObj_[currentPBO_].maxSize = bufSize; } + render_->BindPixelPackBuffer(pixelBufObj_[currentPBO_].buffer); // 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); @@ -899,9 +871,8 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { currentPBO_ = nextPBO; if (unbind) { - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + render_->BindPixelPackBuffer(0); } - CHECK_GL_ERROR_IF_DEBUG(); } void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { @@ -995,26 +966,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() { @@ -1028,7 +982,6 @@ void FramebufferManagerGLES::DeviceRestore(Draw::DrawContext *draw) { } void FramebufferManagerGLES::DestroyAllFBOs() { - CHECK_GL_ERROR_IF_DEBUG(); currentRenderVfb_ = 0; displayFramebuf_ = 0; prevDisplayFramebuf_ = 0; @@ -1055,7 +1008,6 @@ void FramebufferManagerGLES::DestroyAllFBOs() { SetNumExtraFBOs(0); DisableState(); - CHECK_GL_ERROR_IF_DEBUG(); } void FramebufferManagerGLES::Resized() { @@ -1066,15 +1018,7 @@ void FramebufferManagerGLES::Resized() { 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) { diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index 57111e3fa2..60c5c841ad 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -38,7 +38,7 @@ class ShaderManagerGLES; // Simple struct for asynchronous PBO readbacks struct AsyncPBO { - uint32_t handle; + GLRBuffer *buffer; u32 maxSize; u32 fb_address; diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 7c8f61d3c5..9596b8c769 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -28,6 +28,9 @@ void GLQueueRunner::DestroyDeviceObjects() { } void GLQueueRunner::RunInitSteps(const std::vector &steps) { + glActiveTexture(GL_TEXTURE0); + GLuint boundTexture = (GLuint)-1; + for (int i = 0; i < steps.size(); i++) { const GLRInitStep &step = steps[i]; switch (step.stepType) { @@ -36,6 +39,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { GLRTexture *tex = step.create_texture.texture; glGenTextures(1, &tex->texture); glBindTexture(tex->target, tex->texture); + boundTexture = tex->texture; break; } case GLRInitStepType::CREATE_BUFFER: @@ -162,6 +166,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } case GLRInitStepType::CREATE_FRAMEBUFFER: { + boundTexture = (GLuint)-1; InitCreateFramebuffer(step); break; } @@ -171,6 +176,10 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { { GLRTexture *tex = step.texture_image.texture; CHECK_GL_ERROR_IF_DEBUG(); + if (boundTexture != step.texture_image.texture->texture) { + glBindTexture(step.texture_image.texture->target, step.texture_image.texture->texture); + boundTexture = step.texture_image.texture->texture; + } glTexImage2D(tex->target, step.texture_image.level, step.texture_image.internalFormat, step.texture_image.width, step.texture_image.height, 0, step.texture_image.format, step.texture_image.type, step.texture_image.data); delete[] step.texture_image.data; CHECK_GL_ERROR_IF_DEBUG(); @@ -182,6 +191,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } default: Crash(); + break; } } } @@ -359,18 +369,30 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glEnable(GL_SCISSOR_TEST); + /* +#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 + */ + glBindVertexArray(globalVAO_); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); GLRFramebuffer *fb = step.render.framebuffer; GLRProgram *curProgram = nullptr; - GLint activeTexture = GL_TEXTURE0; + int activeTexture = 0; + glActiveTexture(GL_TEXTURE0 + activeTexture); int attrMask = 0; - // TODO: We can implement state-filtering locally in this function, to mimic the old gl state tracker, - // to avoid redundant calls. Might be worth it? + // State filtering tracking. + GLuint curArrayBuffer = (GLuint)-1; + GLuint curElemArrayBuffer = (GLuint)-1; auto &commands = step.commands; for (const auto &c : commands) { @@ -415,6 +437,19 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glClear(c.clear.clearMask); glEnable(GL_SCISSOR_TEST); break; + case GLRRenderCommand::INVALIDATE: + { + GLenum attachments[3]; + int count = 0; + if (c.clear.clearMask & GL_COLOR_BUFFER_BIT) + attachments[count++] = GL_COLOR_ATTACHMENT0; + if (c.clear.clearMask & GL_DEPTH_BUFFER_BIT) + attachments[count++] = GL_DEPTH_ATTACHMENT; + if (c.clear.clearMask & GL_STENCIL_BUFFER_BIT) + attachments[count++] = GL_STENCIL_BUFFER_BIT; + glInvalidateFramebuffer(GL_FRAMEBUFFER, count, attachments); + break; + } case GLRRenderCommand::BLENDCOLOR: glBlendColor(c.blendColor.color[0], c.blendColor.color[1], c.blendColor.color[2], c.blendColor.color[3]); break; @@ -518,7 +553,21 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { if (c.texture.texture) { glBindTexture(c.texture.texture->target, c.texture.texture->texture); } else { - glBindTexture(GL_TEXTURE_2D, 0); // ? + glBindTexture(GL_TEXTURE_2D, 0); // Which target? Well we only use this one anyway... + } + break; + } + case GLRRenderCommand::BIND_FB_TEXTURE: + { + GLint slot = c.bind_fb_texture.slot; + if (slot != activeTexture) { + glActiveTexture(GL_TEXTURE0 + slot); + activeTexture = slot; + } + if (c.bind_fb_texture.aspect == GL_COLOR_BUFFER_BIT) { + glBindTexture(GL_TEXTURE_2D, c.bind_fb_texture.framebuffer->color_texture); + } else { + // TODO: Depth texturing? } break; } @@ -549,19 +598,28 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } break; } - case GLRRenderCommand::BIND_VERTEX_BUFFER: + case GLRRenderCommand::BIND_BUFFER: { - GLuint buf = c.bind_buffer.buffer ? c.bind_buffer.buffer->buffer : 0; - glBindBuffer(GL_ARRAY_BUFFER, buf); - break; - } - case GLRRenderCommand::BIND_INDEX_BUFFER: - { - GLuint buf = c.bind_buffer.buffer ? c.bind_buffer.buffer->buffer : 0; - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf); + if (c.bind_buffer.target == GL_ARRAY_BUFFER) { + GLuint buf = c.bind_buffer.buffer ? c.bind_buffer.buffer->buffer : 0; + if (buf != curArrayBuffer) { + glBindBuffer(GL_ARRAY_BUFFER, buf); + curArrayBuffer = buf; + } + } else if (c.bind_buffer.target == GL_ELEMENT_ARRAY_BUFFER) { + GLuint buf = c.bind_buffer.buffer ? c.bind_buffer.buffer->buffer : 0; + if (buf != curElemArrayBuffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf); + curElemArrayBuffer = buf; + } + } else { + GLuint buf = c.bind_buffer.buffer ? c.bind_buffer.buffer->buffer : 0; + glBindBuffer(c.bind_buffer.target, buf); + } break; } case GLRRenderCommand::GENMIPS: + // TODO: Should we include the texture handle in the command? glGenerateMipmap(GL_TEXTURE_2D); break; case GLRRenderCommand::DRAW: @@ -614,12 +672,17 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } } - if (activeTexture != GL_TEXTURE0) + if (activeTexture != 0) glActiveTexture(GL_TEXTURE0); + + // Wipe out the current state. glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindVertexArray(0); glDisable(GL_SCISSOR_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } void GLQueueRunner::PerformCopy(const GLRStep &step) { @@ -693,58 +756,15 @@ void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) { curFBHeight_ = targetHeight_; } -#if 0 - - CHECK_GL_ERROR_IF_DEBUG(); - curFB_ = (OpenGLFramebuffer *)fbo; - if (fbo) { - OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; + curFB_ = pass.render.framebuffer; + if (curFB_) { // Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing // in ES 2.0 that actually separate them anyway of course, so doesn't matter. - fbo_bind_fb_target(false, fb->handle); - // Always restore viewport after render target binding. Works around driver bugs. - glstate.viewport.restore(); + fbo_bind_fb_target(false, curFB_->handle); } else { fbo_unbind(); + // Backbuffer is now bound. } - int clearFlags = 0; - if (rp.color == RPAction::CLEAR) { - float fc[4]{}; - if (rp.clearColor) { - Uint8x4ToFloat4(fc, rp.clearColor); - } - glClearColor(fc[0], fc[1], fc[2], fc[3]); - clearFlags |= GL_COLOR_BUFFER_BIT; - glstate.colorMask.force(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - } - if (rp.depth == RPAction::CLEAR) { -#ifdef USING_GLES2 - glClearDepthf(rp.clearDepth); -#else - glClearDepth(rp.clearDepth); -#endif - glClearStencil(rp.clearStencil); - clearFlags |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; - glstate.depthWrite.force(GL_TRUE); - glstate.stencilFunc.force(GL_ALWAYS, 0, 0); - glstate.stencilMask.force(0xFF); - } - if (clearFlags) { - glstate.scissorTest.force(false); - glClear(clearFlags); - glstate.scissorTest.restore(); - } - if (rp.color == RPAction::CLEAR) { - glstate.colorMask.restore(); - } - if (rp.depth == RPAction::CLEAR) { - glstate.depthWrite.restore(); - glstate.stencilFunc.restore(); - glstate.stencilMask.restore(); - } - CHECK_GL_ERROR_IF_DEBUG(); -#endif - } void GLQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) { @@ -761,7 +781,6 @@ GLuint GLQueueRunner::AllocTextureName() { return name; } - // On PC, we always use GL_DEPTH24_STENCIL8. // On Android, we try to use what's available. @@ -847,6 +866,7 @@ GLenum GLQueueRunner::fbo_get_fb_target(bool read, GLuint **cached) { } void GLQueueRunner::fbo_bind_fb_target(bool read, GLuint name) { + CHECK_GL_ERROR_IF_DEBUG(); GLuint *cached; GLenum target = fbo_get_fb_target(read, &cached); if (*cached != name) { @@ -859,9 +879,11 @@ void GLQueueRunner::fbo_bind_fb_target(bool read, GLuint name) { } *cached = name; } + CHECK_GL_ERROR_IF_DEBUG(); } void GLQueueRunner::fbo_unbind() { + CHECK_GL_ERROR_IF_DEBUG(); #ifndef USING_GLES2 if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); @@ -878,6 +900,7 @@ void GLQueueRunner::fbo_unbind() { currentDrawHandle_ = 0; currentReadHandle_ = 0; + CHECK_GL_ERROR_IF_DEBUG(); } GLRFramebuffer::~GLRFramebuffer() { diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index e01a80075a..e4c221b8e1 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -41,12 +41,12 @@ enum class GLRRenderCommand : uint8_t { SCISSOR, RASTER, CLEAR, + INVALIDATE, BINDPROGRAM, BINDTEXTURE, BIND_FB_TEXTURE, BIND_INPUT_LAYOUT, - BIND_VERTEX_BUFFER, - BIND_INDEX_BUFFER, + BIND_BUFFER, GENMIPS, DRAW, DRAW_INDEXED, @@ -129,6 +129,7 @@ struct GLRRenderData { } bind_fb_texture; struct { GLRBuffer *buffer; + GLuint target; } bind_buffer; struct { GLRProgram *program; @@ -335,6 +336,7 @@ private: GLenum fbo_get_fb_target(bool read, GLuint **cached); void fbo_unbind(); + GLRFramebuffer *curFB_ = nullptr; GLuint globalVAO_; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index e00f11f7b3..6bbae74b09 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -382,8 +382,6 @@ void GLPushBuffer::Unmap() { // 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. - if (offset_ > 0 && buffers_[buf_].deviceMemory[0] == 0xCD) - Crash(); render_->BufferSubdata(buffers_[buf_].buffer, 0, offset_, buffers_[buf_].deviceMemory, false); writePtr_ = nullptr; } diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 34bc3d238b..c7dcc0b805 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -33,7 +33,6 @@ public: int height; GLuint colorDepth; - GLuint framebuf = 0; bool z_stencil_; }; @@ -330,21 +329,32 @@ public: 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 BindVertexBuffer(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_VERTEX_BUFFER }; + GLRRenderData data{ GLRRenderCommand::BIND_BUFFER }; data.bind_buffer.buffer = buffer; + data.bind_buffer.target = GL_ARRAY_BUFFER; + 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_INDEX_BUFFER}; + GLRRenderData data{ GLRRenderCommand::BIND_BUFFER}; data.bind_buffer.buffer = buffer; + data.bind_buffer.target = GL_ELEMENT_ARRAY_BUFFER; curRenderStep_->commands.push_back(data); } @@ -545,6 +555,13 @@ public: 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 }; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index ee6b366516..5b8ce4b45d 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -1269,7 +1269,13 @@ void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBCh OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; GLuint aspect = 0; - renderManager_.BindFramebufferAsTexture(fb->framebuffer, binding, (int)channelBit, color); + if (channelBit & FB_COLOR_BIT) + aspect |= GL_COLOR_BUFFER_BIT; + if (channelBit & FB_DEPTH_BIT) + aspect |= GL_DEPTH_BUFFER_BIT; + if (channelBit & FB_STENCIL_BIT) + aspect |= GL_STENCIL_BUFFER_BIT; + renderManager_.BindFramebufferAsTexture(fb->framebuffer, binding, aspect, color); } void OpenGLContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { From 9094410fd17e0c8acc9e59ae16feb0d8ef2448cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 14 Dec 2017 16:08:55 +0100 Subject: [PATCH 17/96] gl-render-manager: Need to actually bind newly created textures. --- GPU/GLES/TextureCacheGLES.cpp | 2 ++ ext/native/thin3d/GLQueueRunner.cpp | 45 +++++++++++++++++---------- ext/native/thin3d/GLRenderManager.cpp | 11 +++++-- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index ddbc29b4bf..b6cd1cc038 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -690,6 +690,8 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag } // 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); } diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 9596b8c769..dbfa828426 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -337,25 +337,31 @@ void GLQueueRunner::LogSteps(const std::vector &steps) { void GLQueueRunner::PerformBlit(const GLRStep &step) { - /* // Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing // in ES 2.0 that actually separate them anyway of course, so doesn't matter. - fbo_bind_fb_target(false, dst->handle); - fbo_bind_fb_target(true, src->handle); - if (gl_extensions.GLES3 || gl_extensions.ARB_framebuffer_object) { - glBlitFramebuffer(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, aspect, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); - CHECK_GL_ERROR_IF_DEBUG(); - #if defined(USING_GLES2) && defined(__ANDROID__) // We only support this extension on Android, it's not even available on PC. - return true; - } else if (gl_extensions.NV_framebuffer_blit) { - glBlitFramebufferNV(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, aspect, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); - CHECK_GL_ERROR_IF_DEBUG(); - #endif // defined(USING_GLES2) && defined(__ANDROID__) - return true; - } else { - return false; - }*/ + fbo_bind_fb_target(false, step.blit.dst->handle); + fbo_bind_fb_target(true, step.blit.src->handle); + int srcX1 = step.blit.srcRect.x; + int srcY1 = step.blit.srcRect.y; + int srcX2 = step.blit.srcRect.x + step.blit.srcRect.w; + int srcY2 = step.blit.srcRect.y + step.blit.srcRect.h; + int dstX1 = step.blit.dstRect.x; + int dstY1 = step.blit.dstRect.y; + int dstX2 = step.blit.dstRect.x + step.blit.dstRect.w; + int dstY2 = step.blit.dstRect.y + step.blit.dstRect.h; + + if (gl_extensions.GLES3 || gl_extensions.ARB_framebuffer_object) { + glBlitFramebuffer(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, step.blit.aspectMask, step.blit.filter ? GL_LINEAR : GL_NEAREST); + CHECK_GL_ERROR_IF_DEBUG(); +#if defined(USING_GLES2) && defined(__ANDROID__) // We only support this extension on Android, it's not even available on PC. + } else if (gl_extensions.NV_framebuffer_blit) { + glBlitFramebufferNV(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, step.blit.aspectMask, step.blit.filter ? GL_LINEAR : GL_NEAREST); + CHECK_GL_ERROR_IF_DEBUG(); +#endif // defined(USING_GLES2) && defined(__ANDROID__) + } else { + ERROR_LOG(G3D, "GLQueueRunner: Tried to blit without the capability"); + } } void GLQueueRunner::PerformRenderPass(const GLRStep &step) { @@ -367,6 +373,11 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { PerformBindFramebufferAsRenderTarget(step); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_BLEND); + glDisable(GL_CULL_FACE); glEnable(GL_SCISSOR_TEST); /* @@ -681,7 +692,9 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glBindVertexArray(0); glDisable(GL_SCISSOR_TEST); glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); glDisable(GL_BLEND); + glDisable(GL_CULL_FACE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 6bbae74b09..2ccccd5adb 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -200,13 +200,19 @@ void GLRenderManager::CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLR step->copy.dstPos = dstPos; step->copy.src = src; step->copy.dst = dst; - step->copy.dstPos = dstPos; step->copy.aspectMask = aspectMask; steps_.push_back(step); } void GLRenderManager::BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLRect2D dstRect, int aspectMask, bool filter) { - Crash(); + 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); } void GLRenderManager::BeginFrame() { @@ -229,6 +235,7 @@ void GLRenderManager::BeginFrame() { // 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); From d5c6786ead15624800fb9d3cee5ba3a0b9a82dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 14 Dec 2017 16:36:05 +0100 Subject: [PATCH 18/96] Fix some texture memory bugs (yeah need to make copies..) --- GPU/GLES/DepalettizeShaderGLES.cpp | 4 +++- GPU/GLES/DrawEngineGLES.cpp | 10 +++++++++- GPU/GLES/FragmentTestCacheGLES.cpp | 7 +++---- GPU/GLES/FragmentTestCacheGLES.h | 1 - 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/GPU/GLES/DepalettizeShaderGLES.cpp b/GPU/GLES/DepalettizeShaderGLES.cpp index 6ea73043a1..63fc9d05bd 100644 --- a/GPU/GLES/DepalettizeShaderGLES.cpp +++ b/GPU/GLES/DepalettizeShaderGLES.cpp @@ -93,7 +93,9 @@ GLRTexture *DepalShaderCacheGLES::GetClutTexture(GEPaletteFormat clutFormat, con GLuint components = dstFmt == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA; GLuint components2 = components; - render_->TextureImage(tex->texture, 0, texturePixels, 1, components, components2, dstFmt, (uint8_t *)rawClut, false); + 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; diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 92efcc43fb..d6161e4335 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -564,7 +564,15 @@ void DrawEngineGLES::DoFlush() { vai->lastFrame = gpuStats.numFlips; } else { - DecodeVertsToPushBuffer(frameData.pushVertex, &vertexBufferOffset, &vertexBuffer); + 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(); diff --git a/GPU/GLES/FragmentTestCacheGLES.cpp b/GPU/GLES/FragmentTestCacheGLES.cpp index 4098186d20..0ad098a499 100644 --- a/GPU/GLES/FragmentTestCacheGLES.cpp +++ b/GPU/GLES/FragmentTestCacheGLES.cpp @@ -29,12 +29,10 @@ static const int FRAGTEST_DECIMATION_INTERVAL = 113; FragmentTestCacheGLES::FragmentTestCacheGLES(Draw::DrawContext *draw) { render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - scratchpad_ = new u8[256 * 4]; } FragmentTestCacheGLES::~FragmentTestCacheGLES() { Clear(); - delete [] scratchpad_; } void FragmentTestCacheGLES::BindTestTexture(int slot) { @@ -100,6 +98,7 @@ FragmentTestID FragmentTestCacheGLES::GenerateTestID() const { } 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. @@ -135,12 +134,12 @@ GLRTexture *FragmentTestCacheGLES::CreateTestTexture(const GEComparison funcs[4] break; } } - scratchpad_[color * 4 + i] = res ? 0xFF : 0; + data[color * 4 + i] = res ? 0xFF : 0; } } GLRTexture *tex = render_->CreateTexture(GL_TEXTURE_2D); - render_->TextureImage(tex, 0, 256, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, scratchpad_); + render_->TextureImage(tex, 0, 256, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, data); return tex; } diff --git a/GPU/GLES/FragmentTestCacheGLES.h b/GPU/GLES/FragmentTestCacheGLES.h index 9b6ac1d1be..043b236fe6 100644 --- a/GPU/GLES/FragmentTestCacheGLES.h +++ b/GPU/GLES/FragmentTestCacheGLES.h @@ -79,7 +79,6 @@ private: TextureCacheGLES *textureCache_; std::map cache_; - u8 *scratchpad_ = nullptr; GLRTexture *lastTexture_ = nullptr; int decimationCounter_ = 0; }; From 1241abc887b8d27edacf6d21ca113f6cf2d7281d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 14 Dec 2017 16:54:21 +0100 Subject: [PATCH 19/96] Make sure we set GL_TEXTURE_MAX_LEVEL somewhere. --- GPU/GLES/TextureCacheGLES.cpp | 2 ++ ext/native/thin3d/GLQueueRunner.cpp | 24 +++++++++++++++++------- ext/native/thin3d/GLQueueRunner.h | 5 +++++ ext/native/thin3d/GLRenderManager.h | 7 +++++++ 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index b6cd1cc038..e7bd03f2d5 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -689,6 +689,8 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag entry->SetAlphaStatus(TexCacheEntry::TexStatus(replaced.AlphaStatus())); } + render_->FinalizeTexture(entry->textureName, texMaxLevel); + // 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); diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index dbfa828426..d7870682d0 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -176,17 +176,27 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { { GLRTexture *tex = step.texture_image.texture; CHECK_GL_ERROR_IF_DEBUG(); - if (boundTexture != step.texture_image.texture->texture) { - glBindTexture(step.texture_image.texture->target, step.texture_image.texture->texture); - boundTexture = step.texture_image.texture->texture; + if (boundTexture != tex->texture) { + glBindTexture(tex->target, tex->texture); + boundTexture = tex->texture; } glTexImage2D(tex->target, step.texture_image.level, step.texture_image.internalFormat, step.texture_image.width, step.texture_image.height, 0, step.texture_image.format, step.texture_image.type, step.texture_image.data); delete[] step.texture_image.data; CHECK_GL_ERROR_IF_DEBUG(); - 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, step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST); + glTexParameteri(step.texture_finalize.texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(step.texture_finalize.texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(step.texture_finalize.texture->target, GL_TEXTURE_MAG_FILTER, step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST); + glTexParameteri(step.texture_finalize.texture->target, GL_TEXTURE_MIN_FILTER, step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST); + break; + } + case GLRInitStepType::TEXTURE_FINALIZE: + { + GLRTexture *tex = step.texture_finalize.texture; + if (boundTexture != tex->texture) { + glBindTexture(tex->target, tex->texture); + boundTexture = tex->texture; + } + glTexParameteri(step.texture_finalize.texture->target, GL_TEXTURE_MAX_LEVEL, step.texture_finalize.maxLevel); break; } default: diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index e4c221b8e1..d155dbe9be 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -178,6 +178,7 @@ enum class GLRInitStepType : uint8_t { TEXTURE_IMAGE, TEXTURE_SUBDATA, + TEXTURE_FINALIZE, BUFFER_SUBDATA, }; @@ -229,6 +230,10 @@ struct GLRInitStep { bool linearFilter; uint8_t *data; // owned, delete[]-d } texture_image; + struct { + GLRTexture *texture; + int maxLevel; + } texture_finalize; }; }; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index c7dcc0b805..04d5d165a8 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -317,6 +317,13 @@ public: initSteps_.push_back(step); } + void FinalizeTexture(GLRTexture *texture, int maxLevels) { + GLRInitStep step{ GLRInitStepType::TEXTURE_FINALIZE }; + step.texture_finalize.texture = texture; + step.texture_finalize.maxLevel = maxLevels; + initSteps_.push_back(step); + } + void BindTexture(int slot, GLRTexture *tex) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData data{ GLRRenderCommand::BINDTEXTURE }; From 7c17cb675417626d29b4d7c6370937f114427cf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 14 Dec 2017 17:08:45 +0100 Subject: [PATCH 20/96] Fix showing savestate screenshots (needed mips) --- GPU/GLES/TextureCacheGLES.cpp | 7 ++++--- ext/native/thin3d/GLQueueRunner.cpp | 13 ++++++++----- ext/native/thin3d/GLQueueRunner.h | 1 + ext/native/thin3d/GLRenderManager.h | 9 ++------- ext/native/thin3d/thin3d_gl.cpp | 16 +++++----------- 5 files changed, 20 insertions(+), 26 deletions(-) diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index e7bd03f2d5..fffdc1da9e 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -651,12 +651,13 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag // 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) { - render_->GenerateMipmap(); + genMips = true; } else { texMaxLevel = 0; maxLevel = 0; @@ -671,7 +672,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag // 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); - render_->GenerateMipmap(); + genMips = true; } else { maxLevel = 0; } @@ -689,7 +690,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag entry->SetAlphaStatus(TexCacheEntry::TexStatus(replaced.AlphaStatus())); } - render_->FinalizeTexture(entry->textureName, texMaxLevel); + 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. diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index d7870682d0..7fbff23834 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -183,10 +183,10 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { glTexImage2D(tex->target, step.texture_image.level, step.texture_image.internalFormat, step.texture_image.width, step.texture_image.height, 0, step.texture_image.format, step.texture_image.type, step.texture_image.data); delete[] step.texture_image.data; CHECK_GL_ERROR_IF_DEBUG(); - glTexParameteri(step.texture_finalize.texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(step.texture_finalize.texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(step.texture_finalize.texture->target, GL_TEXTURE_MAG_FILTER, step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST); - glTexParameteri(step.texture_finalize.texture->target, GL_TEXTURE_MIN_FILTER, step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST); + glTexParameteri(tex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST); + glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST); break; } case GLRInitStepType::TEXTURE_FINALIZE: @@ -196,7 +196,10 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { glBindTexture(tex->target, tex->texture); boundTexture = tex->texture; } - glTexParameteri(step.texture_finalize.texture->target, GL_TEXTURE_MAX_LEVEL, step.texture_finalize.maxLevel); + glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, step.texture_finalize.maxLevel); + if (step.texture_finalize.genMips) { + glGenerateMipmap(tex->target); + } break; } default: diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index d155dbe9be..b813b77026 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -233,6 +233,7 @@ struct GLRInitStep { struct { GLRTexture *texture; int maxLevel; + bool genMips; } texture_finalize; }; }; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 04d5d165a8..0cab4cb7ca 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -317,10 +317,11 @@ public: initSteps_.push_back(step); } - void FinalizeTexture(GLRTexture *texture, int maxLevels) { + 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); } @@ -374,12 +375,6 @@ public: curRenderStep_->commands.push_back(data); } - void GenerateMipmap() { - _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); - GLRRenderData data{ GLRRenderCommand::GENMIPS }; - 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 }; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 5b8ce4b45d..dec2b0b5a3 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -598,8 +598,6 @@ public: render_->BindTexture(stage, tex_); } - void AutoGenMipmaps(); - private: void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data); @@ -638,10 +636,14 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : } mipLevels_ = desc.generateMips ? desc.mipLevels : level; + bool genMips = false; if ((int)desc.initData.size() < desc.mipLevels && desc.generateMips) { ILOG("Generating mipmaps"); - AutoGenMipmaps(); + // Assumes the texture is bound for editing + genMips = true; + generatedMips_ = true; } + render->FinalizeTexture(tex_, mipLevels_, genMips); } @@ -653,14 +655,6 @@ OpenGLTexture::~OpenGLTexture() { } } -void OpenGLTexture::AutoGenMipmaps() { - if (!generatedMips_) { - // Assumes the texture is bound for editing - render_->GenerateMipmap(); - generatedMips_ = true; - } -} - class OpenGLFramebuffer : public Framebuffer { public: OpenGLFramebuffer(GLRenderManager *render) : render_(render) {} From 970458a0c293f7fbba6520ed9e22156316d09983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 14 Dec 2017 17:55:56 +0100 Subject: [PATCH 21/96] Scissor fix --- ext/native/thin3d/GLQueueRunner.h | 4 ++-- ext/native/thin3d/thin3d_gl.cpp | 8 +------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index b813b77026..1c805f3eed 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -344,7 +344,7 @@ private: GLRFramebuffer *curFB_ = nullptr; - GLuint globalVAO_; + GLuint globalVAO_ = 0; GLint curFramebuffer_ = 0; int curFBWidth_ = 0; @@ -357,7 +357,7 @@ private: GLint readbackBuffer_ = 0; int readbackBufferSize_ = 0; - float maxAnisotropyLevel_; + float maxAnisotropyLevel_ = 0.0f; // Framebuffer state? GLuint currentDrawHandle_ = 0; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index dec2b0b5a3..05aed08ef1 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -395,12 +395,7 @@ public: } void SetScissorRect(int left, int top, int width, int height) override { - int y = top; - if (!curFB_) { - // We render "upside down" to the backbuffer since GL is silly. - y = targetHeight_ - (top + height); - } - renderManager_.SetScissor({ left, y, width, height }); + renderManager_.SetScissor({ left, top, width, height }); } void SetViewports(int count, Viewport *viewports) override { @@ -492,7 +487,6 @@ private: int curVBufferOffsets_[4]{}; OpenGLBuffer *curIBuffer_ = nullptr; int curIBufferOffset_ = 0; - OpenGLFramebuffer *curFB_; // Frames in flight is not such a strict concept as with Vulkan until we start using glBufferStorage and fences. // But might as well have the structure ready, and can't hurt to rotate buffers. From f99fa02ba72146b0803f01371669bdf81aca0bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 14 Dec 2017 17:50:40 +0100 Subject: [PATCH 22/96] Run the depal stuff, seems a bit broken. Add some state filtering. --- CMakeLists.txt | 5 +- GPU/GLES/DepalettizeShaderGLES.cpp | 6 +- GPU/GLES/DepalettizeShaderGLES.h | 2 - GPU/GLES/DrawEngineGLES.cpp | 24 +++++-- GPU/GLES/FramebufferManagerGLES.cpp | 2 +- GPU/GLES/StateMappingGLES.cpp | 9 --- GPU/GLES/TextureCacheGLES.cpp | 101 ++++++++++------------------ GPU/GLES/TextureCacheGLES.h | 2 + ext/native/thin3d/GLQueueRunner.cpp | 38 ++++++++--- ext/native/thin3d/GLQueueRunner.h | 2 +- ext/native/thin3d/GLRenderManager.h | 16 +++-- ext/native/thin3d/thin3d_gl.cpp | 10 +-- 12 files changed, 105 insertions(+), 112 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b60032d8f6..9aeeed988f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -830,7 +830,10 @@ 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/GLQueueRunner.cpp) + set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} ext/native/thin3d/thin3d_vulkan.cpp ext/native/thin3d/VulkanRenderManager.cpp diff --git a/GPU/GLES/DepalettizeShaderGLES.cpp b/GPU/GLES/DepalettizeShaderGLES.cpp index 63fc9d05bd..95f58f37c6 100644 --- a/GPU/GLES/DepalettizeShaderGLES.cpp +++ b/GPU/GLES/DepalettizeShaderGLES.cpp @@ -167,8 +167,8 @@ DepalShader *DepalShaderCacheGLES::GetDepalettizeShader(uint32_t clutMode, GEBuf queries.push_back({ &depal->u_pal, "pal" }); std::vector initializer; - initializer.push_back({ &depal->u_tex, 0 }); - initializer.push_back({ &depal->u_pal, 3 }); + initializer.push_back({ &depal->u_tex, 0, 0 }); + initializer.push_back({ &depal->u_pal, 0, 3 }); std::vector shaders{ vertexShader_, fragShader }; @@ -177,8 +177,6 @@ DepalShader *DepalShaderCacheGLES::GetDepalettizeShader(uint32_t clutMode, GEBuf depal->program = program; depal->fragShader = fragShader; depal->code = buffer; - depal->a_position = 0; - depal->a_texcoord0 = 1; cache_[id] = depal; delete[] buffer; diff --git a/GPU/GLES/DepalettizeShaderGLES.h b/GPU/GLES/DepalettizeShaderGLES.h index bc3e93facd..6fd9749fec 100644 --- a/GPU/GLES/DepalettizeShaderGLES.h +++ b/GPU/GLES/DepalettizeShaderGLES.h @@ -29,8 +29,6 @@ class DepalShader { public: GLRProgram *program; GLRShader *fragShader; - GLint a_position; - GLint a_texcoord0; GLint u_tex; GLint u_pal; std::string code; diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index d6161e4335..166347016a 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -401,8 +401,14 @@ void DrawEngineGLES::DoFlush() { 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); VShaderID vsid; Shader *vshader = shaderManager_->ApplyVertexShader(prim, lastVType_, &vsid); @@ -592,12 +598,17 @@ rotateVBO: gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255); } + 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); GLRInputLayout *inputLayout = SetupDecFmtForDraw(program, dec_->GetDecVtxFmt()); render_->BindVertexBuffer(vertexBuffer); - render_->BindInputLayout(inputLayout, (void *)(uintptr_t)vertexBufferOffset); + render_->BindInputLayout(inputLayout, vertexBufferOffset); if (useElements) { if (!indexBuffer) { indexBufferOffset = (uint32_t)frameData.pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &indexBuffer); @@ -653,6 +664,11 @@ rotateVBO: prim, vertexCount, dec_->VertexType(), inds, GE_VTYPE_IDX_16BIT, dec_->GetDecVtxFmt(), maxIndex, drawBuffer, numTrans, drawIndexed, ¶ms, &result); + + if (textureNeedsApply) + textureCache_->ApplyTexture(); + + ApplyDrawState(prim); ApplyDrawStateLate(result.setStencil, result.stencilValue); LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, prim); @@ -666,13 +682,13 @@ rotateVBO: 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(vertexBuffer); - render_->BindInputLayout(softwareInputLayout_, (void *)(intptr_t)vertexBufferOffset); + render_->BindInputLayout(softwareInputLayout_, vertexBufferOffset); render_->BindIndexBuffer(indexBuffer); render_->DrawIndexed(glprim[prim], numTrans, GL_UNSIGNED_SHORT, (void *)(intptr_t)indexBufferOffset); } else { vertexBufferOffset = (uint32_t)frameData.pushVertex->Push(drawBuffer, numTrans * sizeof(TransformedVertex), &vertexBuffer); render_->BindVertexBuffer(vertexBuffer); - render_->BindInputLayout(softwareInputLayout_, (void *)(intptr_t)vertexBufferOffset); + render_->BindInputLayout(softwareInputLayout_, vertexBufferOffset); render_->Draw(glprim[prim], 0, numTrans); } } else if (result.action == SW_CLEAR) { diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 2f033d736c..76acf62990 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -466,7 +466,7 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float void *dest = drawEngineGL_->GetPushVertexBuffer()->Push(sizeof(verts), &bindOffset, &buffer); memcpy(dest, verts, sizeof(verts)); render_->BindVertexBuffer(buffer); - render_->BindInputLayout(simple2DInputLayout_, (void *)(intptr_t)bindOffset); + render_->BindInputLayout(simple2DInputLayout_, bindOffset); render_->Draw(GL_TRIANGLE_STRIP, 0, 4); } diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 11b7e79576..932d148656 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -132,11 +132,6 @@ inline void DrawEngineGLES::ResetShaderBlending() { void DrawEngineGLES::ApplyDrawState(int prim) { GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - 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); - } - if (!gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE)) { // Nothing to do, let's early-out return; @@ -341,10 +336,6 @@ void DrawEngineGLES::ApplyDrawStateLate(bool setStencil, int stencilValue) { fboTexNeedBind_ = false; } - // 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(); - // Apply last, once we know the alpha params of the texture. if (gstate.isAlphaTestEnabled() || gstate.isColorTestEnabled()) { fragmentTestCache_->BindTestTexture(2); diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index fffdc1da9e..652082d148 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -53,9 +53,14 @@ TextureCacheGLES::TextureCacheGLES(Draw::DrawContext *draw) SetupTextureDecoder(); nextTexture_ = nullptr; + std::vector 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); } @@ -65,7 +70,7 @@ 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) { render_->DeleteTexture(entry->textureName); @@ -322,21 +327,11 @@ void TextureCacheGLES::Unbind() { 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; }; @@ -379,69 +374,45 @@ 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(GLRenderManager *render, DrawEngineGLES *transformDraw) { + void Use(GLRenderManager *render, DrawEngineGLES *transformDraw, GLRInputLayout *inputLayout) { render->BindProgram(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); + 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(bindBuffer); + render->BindInputLayout(inputLayout, 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: @@ -469,14 +440,13 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram TextureShaderApplier shaderApply(depal, framebuffer->bufferWidth, framebuffer->bufferHeight, framebuffer->renderWidth, framebuffer->renderHeight); shaderApply.ApplyBounds(gstate_c.vertBounds, gstate_c.curTextureXOffset, gstate_c.curTextureYOffset); - shaderApply.Use(render_, drawEngine_); - - render_->BindTexture(3, clutTexture); + shaderApply.Use(render_, drawEngine_, shadeInputLayout_); framebufferManagerGL_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_SKIP_COPY); + 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); @@ -497,6 +467,9 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight); 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) { diff --git a/GPU/GLES/TextureCacheGLES.h b/GPU/GLES/TextureCacheGLES.h index dcefcfb700..2d415c599a 100644 --- a/GPU/GLES/TextureCacheGLES.h +++ b/GPU/GLES/TextureCacheGLES.h @@ -101,6 +101,8 @@ private: ShaderManagerGLES *shaderManager_; DrawEngineGLES *drawEngine_; + GLRInputLayout *shadeInputLayout_; + enum { INVALID_TEX = -1 }; }; diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 7fbff23834..0f7288ea3c 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -82,11 +82,9 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { glBindFragDataLocation(program->program, 0, "fragColor0"); } #elif !defined(IOS) - if (gl_extensions.GLES3) { - if (gstate_c.featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND) { - glBindFragDataLocationIndexedEXT(program->program, 0, 0, "fragColor0"); - glBindFragDataLocationIndexedEXT(program->program, 0, 1, "fragColor1"); - } + if (gl_extensions.GLES3 && step.create_program.support_dual_source) { + glBindFragDataLocationIndexedEXT(program->program, 0, 0, "fragColor0"); + glBindFragDataLocationIndexedEXT(program->program, 0, 1, "fragColor1"); } #endif glLinkProgram(program->program); @@ -413,7 +411,9 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glActiveTexture(GL_TEXTURE0 + activeTexture); int attrMask = 0; - + int colorMask = -1; + int depthMask = -1; + int depthFunc = -1; // State filtering tracking. GLuint curArrayBuffer = (GLuint)-1; GLuint curElemArrayBuffer = (GLuint)-1; @@ -424,8 +424,14 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { case GLRRenderCommand::DEPTH: if (c.depth.enabled) { glEnable(GL_DEPTH_TEST); - glDepthMask(c.depth.write); - glDepthFunc(c.depth.func); + if (c.depth.write != depthMask) { + glDepthMask(c.depth.write); + depthMask = c.depth.write; + } + if (c.depth.func != depthFunc) { + glDepthFunc(c.depth.func); + depthFunc = c.depth.func; + } } else { glDisable(GL_DEPTH_TEST); } @@ -438,11 +444,15 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } else { glDisable(GL_BLEND); } - glColorMask(c.blend.mask & 1, (c.blend.mask >> 1) & 1, (c.blend.mask >> 2) & 1, (c.blend.mask >> 3) & 1); + if (c.blend.mask != colorMask) { + glColorMask(c.blend.mask & 1, (c.blend.mask >> 1) & 1, (c.blend.mask >> 2) & 1, (c.blend.mask >> 3) & 1); + colorMask = c.blend.mask; + } break; case GLRRenderCommand::CLEAR: glDisable(GL_SCISSOR_TEST); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + colorMask = 0xF; if (c.clear.clearMask & GL_COLOR_BUFFER_BIT) { float color[4]; Uint8x4ToFloat4(color, c.clear.clearColor); @@ -485,7 +495,11 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { // TODO: Support FP viewports through glViewportArrays glViewport((GLint)c.viewport.vp.x, (GLint)y, (GLsizei)c.viewport.vp.w, (GLsizei)c.viewport.vp.h); +#if !defined(USING_GLES2) glDepthRange(c.viewport.vp.minZ, c.viewport.vp.maxZ); +#else + glDepthRangef(c.viewport.vp.minZ, c.viewport.vp.maxZ); +#endif break; } case GLRRenderCommand::SCISSOR: @@ -597,8 +611,10 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } case GLRRenderCommand::BINDPROGRAM: { - glUseProgram(c.program.program->program); - curProgram = c.program.program; + if (curProgram != c.program.program) { + glUseProgram(c.program.program->program); + curProgram = c.program.program; + } break; } case GLRRenderCommand::BIND_INPUT_LAYOUT: diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index 1c805f3eed..56e2d507eb 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -136,7 +136,7 @@ struct GLRRenderData { } program; struct { GLRInputLayout *inputLayout; - intptr_t offset; + size_t offset; } inputLayout; struct { GLenum wrapS; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 0cab4cb7ca..145e42cc00 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include #include #include "gfx/gl_common.h" @@ -83,8 +85,9 @@ public: }; // Must ONLY be called from GLQueueRunner! + // Also it's pretty slow... int GetUniformLoc(const char *name) { - auto iter = uniformCache_.find(name); + auto iter = uniformCache_.find(std::string(name)); int loc = -1; if (iter != uniformCache_.end()) { loc = iter->second.loc_; @@ -232,6 +235,7 @@ public: 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]; @@ -288,15 +292,15 @@ public: 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, int offset, int size, uint8_t *data, bool 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 = offset; - step.buffer_subdata.size = size; + 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); @@ -366,12 +370,12 @@ public: curRenderStep_->commands.push_back(data); } - void BindInputLayout(GLRInputLayout *inputLayout, const void *offset) { + void BindInputLayout(GLRInputLayout *inputLayout, size_t offset) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); assert(inputLayout); GLRRenderData data{ GLRRenderCommand::BIND_INPUT_LAYOUT }; data.inputLayout.inputLayout = inputLayout; - data.inputLayout.offset = (intptr_t)offset; + data.inputLayout.offset = offset; curRenderStep_->commands.push_back(data); } diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 05aed08ef1..76c8d6b6bd 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -1115,7 +1115,6 @@ void OpenGLContext::DrawIndexed(int vertexCount, int offset) { } void OpenGLContext::DrawUP(const void *vdata, int vertexCount) { -#if 1 int stride = curPipeline_->inputLayout->stride; size_t dataSize = stride * vertexCount; @@ -1127,16 +1126,9 @@ void OpenGLContext::DrawUP(const void *vdata, int vertexCount) { ApplySamplers(); renderManager_.BindVertexBuffer(buf); - renderManager_.BindInputLayout(curPipeline_->inputLayout->inputLayout_, (void *)offset); + renderManager_.BindInputLayout(curPipeline_->inputLayout->inputLayout_, offset); renderManager_.Draw(curPipeline_->prim, 0, vertexCount); -#else - ApplySamplers(); - renderManager_.BindInputLayout(curPipeline_->inputLayout->inputLayout_, (void *)vdata); - renderManager_.Draw(curPipeline_->prim, 0, vertexCount); - renderManager_.UnbindInputLayout(curPipeline_->inputLayout->inputLayout_); - -#endif } void OpenGLContext::Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) { From 84ad7a2dbaf7724ecb40e48943e0b02c4a4577c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 15 Dec 2017 11:05:43 +0100 Subject: [PATCH 23/96] Fix bug, works on Android now. Don't reuse textures. --- GPU/GLES/FramebufferManagerGLES.cpp | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 76acf62990..d4b9263a29 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -98,7 +98,10 @@ void FramebufferManagerGLES::CompileDraw2DProgram() { queries.push_back({ &u_draw2d_tex, "u_tex" }); std::vector initializers; initializers.push_back({ &u_draw2d_tex, 0 }); - draw2dprogram_ = render_->CreateProgram(shaders, {}, queries, initializers, false); + std::vector 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(); @@ -334,24 +337,15 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma } } - if (drawPixelsTex_ && (drawPixelsTexFormat_ != srcPixelFormat || drawPixelsTexW_ != texWidth || drawPixelsTexH_ != height)) { + if (drawPixelsTex_) { render_->DeleteTexture(drawPixelsTex_); - drawPixelsTex_ = nullptr; } - if (!drawPixelsTex_) { - drawPixelsTex_ = render_->CreateTexture(GL_TEXTURE_2D); - drawPixelsTexW_ = texWidth; - drawPixelsTexH_ = height; + drawPixelsTex_ = render_->CreateTexture(GL_TEXTURE_2D); + drawPixelsTexW_ = texWidth; + drawPixelsTexH_ = height; - render_->BindTexture(0, drawPixelsTex_); - render_->SetTextureSampler(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f); - - // render_->TextureImage(drawPixelsTex_, 0, drawPixelsTexW_, drawPixelsTexH_, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - drawPixelsTexFormat_ = srcPixelFormat; - } else { - render_->BindTexture(0, 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. @@ -396,7 +390,8 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma break; } } - render_->TextureImage(drawPixelsTex_, 0, texWidth, height, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, convBuf); + render_->TextureImage(drawPixelsTex_, 0, texWidth, height, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, convBuf, false); + render_->FinalizeTexture(drawPixelsTex_, 0, false); } void FramebufferManagerGLES::SetViewport2D(int x, int y, int w, int h) { From 958078f6032aa2846fa8fdd964218c3e81038c43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 19 Dec 2017 12:25:13 +0100 Subject: [PATCH 24/96] GL render manager: Merge BindInputLayout into BindVertexBuffer. --- GPU/GLES/DrawEngineGLES.cpp | 13 ++++--------- GPU/GLES/FramebufferManagerGLES.cpp | 3 +-- GPU/GLES/TextureCacheGLES.cpp | 3 +-- ext/native/thin3d/GLQueueRunner.cpp | 26 +++++++++++++++----------- ext/native/thin3d/GLQueueRunner.h | 5 +++-- ext/native/thin3d/GLRenderManager.h | 21 +++++++-------------- ext/native/thin3d/thin3d_gl.cpp | 29 +++++++++-------------------- 7 files changed, 40 insertions(+), 60 deletions(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 166347016a..1cf8e67af5 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -514,7 +514,6 @@ void DrawEngineGLES::DoFlush() { size_t vsz = dec_->GetDecVtxFmt().stride * indexGen.MaxIndex(); vai->vbo = render_->CreateBuffer(GL_ARRAY_BUFFER, vsz, GL_STATIC_DRAW); render_->BufferSubdata(vai->vbo, 0, vsz, decoded); - render_->BindVertexBuffer(vai->vbo); // 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. @@ -607,16 +606,14 @@ rotateVBO: LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, lastVType_, prim); GLRInputLayout *inputLayout = SetupDecFmtForDraw(program, dec_->GetDecVtxFmt()); - render_->BindVertexBuffer(vertexBuffer); - render_->BindInputLayout(inputLayout, vertexBufferOffset); + 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, (GLvoid*)(intptr_t)indexBufferOffset, numPatches); + render_->DrawIndexed(glprim[prim], vertexCount, GL_UNSIGNED_SHORT, (GLvoid*)(intptr_t)indexBufferOffset, numPatches); else render_->DrawIndexed(glprim[prim], vertexCount, GL_UNSIGNED_SHORT, (GLvoid*)(intptr_t)indexBufferOffset); } else { @@ -681,14 +678,12 @@ rotateVBO: if (drawIndexed) { 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(vertexBuffer); - render_->BindInputLayout(softwareInputLayout_, vertexBufferOffset); + render_->BindVertexBuffer(softwareInputLayout_, vertexBuffer, vertexBufferOffset); render_->BindIndexBuffer(indexBuffer); render_->DrawIndexed(glprim[prim], numTrans, GL_UNSIGNED_SHORT, (void *)(intptr_t)indexBufferOffset); } else { vertexBufferOffset = (uint32_t)frameData.pushVertex->Push(drawBuffer, numTrans * sizeof(TransformedVertex), &vertexBuffer); - render_->BindVertexBuffer(vertexBuffer); - render_->BindInputLayout(softwareInputLayout_, vertexBufferOffset); + render_->BindVertexBuffer(softwareInputLayout_, vertexBuffer, vertexBufferOffset); render_->Draw(glprim[prim], 0, numTrans); } } else if (result.action == SW_CLEAR) { diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index d4b9263a29..dbfa48c1ad 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -460,8 +460,7 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float GLRBuffer *buffer; void *dest = drawEngineGL_->GetPushVertexBuffer()->Push(sizeof(verts), &bindOffset, &buffer); memcpy(dest, verts, sizeof(verts)); - render_->BindVertexBuffer(buffer); - render_->BindInputLayout(simple2DInputLayout_, bindOffset); + render_->BindVertexBuffer(simple2DInputLayout_, buffer, bindOffset); render_->Draw(GL_TRIANGLE_STRIP, 0, 4); } diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 652082d148..3ff8704bca 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -405,8 +405,7 @@ public: memcpy(verts[i].pos, &pos_[order[i]], sizeof(Pos)); memcpy(verts[i].uv, &uv_[order[i]], sizeof(UV)); } - render->BindVertexBuffer(bindBuffer); - render->BindInputLayout(inputLayout, bindOffset); + render->BindVertexBuffer(inputLayout, bindBuffer, bindOffset); } void Shade(GLRenderManager *render) { diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 0f7288ea3c..63343f6883 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -617,12 +617,17 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } break; } - case GLRRenderCommand::BIND_INPUT_LAYOUT: + case GLRRenderCommand::BIND_VERTEX_BUFFER: { - GLRInputLayout *layout = c.inputLayout.inputLayout; - int enable, disable; - enable = layout->semanticsMask_ & ~attrMask; - disable = (~layout->semanticsMask_) & attrMask; + // TODO: Add fast path for glBindVertexBuffer + GLRInputLayout *layout = c.bindVertexBuffer.inputLayout; + GLuint buf = c.bindVertexBuffer.buffer ? c.bindVertexBuffer.buffer->buffer : 0; + if (buf != curArrayBuffer) { + glBindBuffer(GL_ARRAY_BUFFER, buf); + curArrayBuffer = buf; + } + int enable = layout->semanticsMask_ & ~attrMask; + int disable = (~layout->semanticsMask_) & attrMask; for (int i = 0; i < 7; i++) { // SEM_MAX if (enable & (1 << i)) { glEnableVertexAttribArray(i); @@ -634,18 +639,14 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { attrMask = layout->semanticsMask_; for (int i = 0; i < layout->entries.size(); i++) { auto &entry = layout->entries[i]; - glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, entry.stride, (const void *)(c.inputLayout.offset + entry.offset)); + glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, entry.stride, (const void *)(c.bindVertexBuffer.offset + entry.offset)); } break; } case GLRRenderCommand::BIND_BUFFER: { if (c.bind_buffer.target == GL_ARRAY_BUFFER) { - GLuint buf = c.bind_buffer.buffer ? c.bind_buffer.buffer->buffer : 0; - if (buf != curArrayBuffer) { - glBindBuffer(GL_ARRAY_BUFFER, buf); - curArrayBuffer = buf; - } + Crash(); } else if (c.bind_buffer.target == GL_ELEMENT_ARRAY_BUFFER) { GLuint buf = c.bind_buffer.buffer ? c.bind_buffer.buffer->buffer : 0; if (buf != curElemArrayBuffer) { @@ -660,6 +661,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } case GLRRenderCommand::GENMIPS: // TODO: Should we include the texture handle in the command? + // Also, should this not be an init command? glGenerateMipmap(GL_TEXTURE_2D); break; case GLRRenderCommand::DRAW: @@ -668,6 +670,8 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { case GLRRenderCommand::DRAW_INDEXED: if (c.drawIndexed.instances == 1) { glDrawElements(c.drawIndexed.mode, c.drawIndexed.count, c.drawIndexed.indexType, c.drawIndexed.indices); + } else { + glDrawElementsInstanced(c.drawIndexed.mode, c.drawIndexed.count, c.drawIndexed.indexType, c.drawIndexed.indices, c.drawIndexed.instances); } break; case GLRRenderCommand::TEXTURESAMPLER: diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index 56e2d507eb..16a97f5be0 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -45,7 +45,7 @@ enum class GLRRenderCommand : uint8_t { BINDPROGRAM, BINDTEXTURE, BIND_FB_TEXTURE, - BIND_INPUT_LAYOUT, + BIND_VERTEX_BUFFER, BIND_BUFFER, GENMIPS, DRAW, @@ -136,8 +136,9 @@ struct GLRRenderData { } program; struct { GLRInputLayout *inputLayout; + GLRBuffer *buffer; size_t offset; - } inputLayout; + } bindVertexBuffer; struct { GLenum wrapS; GLenum wrapT; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 145e42cc00..de559fcc31 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -346,14 +346,6 @@ public: curRenderStep_->commands.push_back(data); } - void BindVertexBuffer(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_ARRAY_BUFFER; - 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 }; @@ -370,12 +362,13 @@ public: curRenderStep_->commands.push_back(data); } - void BindInputLayout(GLRInputLayout *inputLayout, size_t offset) { + void BindVertexBuffer(GLRInputLayout *inputLayout, GLRBuffer *buffer, size_t offset) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); assert(inputLayout); - GLRRenderData data{ GLRRenderCommand::BIND_INPUT_LAYOUT }; - data.inputLayout.inputLayout = inputLayout; - data.inputLayout.offset = offset; + GLRRenderData data{ GLRRenderCommand::BIND_VERTEX_BUFFER }; + data.bindVertexBuffer.inputLayout = inputLayout; + data.bindVertexBuffer.offset = offset; + data.bindVertexBuffer.buffer = buffer; curRenderStep_->commands.push_back(data); } @@ -579,13 +572,13 @@ public: curRenderStep_->render.numDraws++; } - void DrawIndexed(GLenum mode, int count, GLenum indexType, void *indices) { + 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 = 1; + data.drawIndexed.instances = instances; data.drawIndexed.indices = indices; curRenderStep_->commands.push_back(data); curRenderStep_->render.numDraws++; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 76c8d6b6bd..f65b765e48 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -940,13 +940,6 @@ public: render_->DeleteBuffer(buffer_); } - void Bind(int offset) { - Crash(); - // render_->BindBuffer(buffer_); - // TODO: Can't support offset using ES 2.0 - // glBindBuffer(target_, buffer_); - } - GLRenderManager *render_; GLRBuffer *buffer_; GLuint target_; @@ -962,7 +955,6 @@ Buffer *OpenGLContext::CreateBuffer(size_t size, uint32_t usageFlags) { void OpenGLContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) { OpenGLBuffer *buf = (OpenGLBuffer *)buffer; - buf->Bind(0); if (size + offset > buf->totalSize_) { Crash(); } @@ -970,6 +962,7 @@ void OpenGLContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t off uint8_t *dataCopy = new uint8_t[size]; memcpy(dataCopy, data, size); // if (flags & UPDATE_DISCARD) we could try to orphan the buffer using glBufferData. + // But we're much better off using separate buffers per FrameData... renderManager_.BufferSubdata(buf->buffer_, offset, size, dataCopy); } @@ -1097,21 +1090,19 @@ void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) { } void OpenGLContext::Draw(int vertexCount, int offset) { - curVBuffers_[0]->Bind(curVBufferOffsets_[0]); - renderManager_.BindInputLayout(curPipeline_->inputLayout->inputLayout_, 0); + _dbg_assert_msg_(G3D, curVBuffers_[0], "Can't call Draw without a vertex buffer"); ApplySamplers(); - + renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, curVBuffers_[0]->buffer_, curVBufferOffsets_[0]); renderManager_.Draw(curPipeline_->prim, offset, vertexCount); } void OpenGLContext::DrawIndexed(int vertexCount, int offset) { - curVBuffers_[0]->Bind(curVBufferOffsets_[0]); - renderManager_.BindInputLayout(curPipeline_->inputLayout->inputLayout_, 0); + _dbg_assert_msg_(G3D, curVBuffers_[0], "Can't call DrawIndexed without a vertex buffer"); + _dbg_assert_msg_(G3D, curIBuffer_, "Can't call DrawIndexed without an index buffer"); ApplySamplers(); - // Note: ibuf binding is stored in the VAO, so call this after binding the fmt. - curIBuffer_->Bind(curIBufferOffset_); - - renderManager_.DrawIndexed(curPipeline_->prim, vertexCount, GL_UNSIGNED_INT, (void *)(intptr_t)offset); + renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, curVBuffers_[0]->buffer_, curVBufferOffsets_[0]); + renderManager_.BindIndexBuffer(curIBuffer_->buffer_); + renderManager_.DrawIndexed(curPipeline_->prim, vertexCount, GL_UNSIGNED_INT, (void *)(intptr_t)curIBufferOffset_); } void OpenGLContext::DrawUP(const void *vdata, int vertexCount) { @@ -1125,9 +1116,7 @@ void OpenGLContext::DrawUP(const void *vdata, int vertexCount) { ApplySamplers(); - renderManager_.BindVertexBuffer(buf); - renderManager_.BindInputLayout(curPipeline_->inputLayout->inputLayout_, offset); - + renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, buf, offset); renderManager_.Draw(curPipeline_->prim, 0, vertexCount); } From 0a413a18a7fa63247de4250f1c96f47b07349733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 19 Dec 2017 12:49:48 +0100 Subject: [PATCH 25/96] GL render manager: Add some more dirty tracking. --- ext/native/thin3d/GLQueueRunner.cpp | 73 ++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 63343f6883..1d9f9ca108 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -389,6 +389,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glDisable(GL_STENCIL_TEST); glDisable(GL_BLEND); glDisable(GL_CULL_FACE); + glDisable(GL_DITHER); glEnable(GL_SCISSOR_TEST); /* @@ -410,20 +411,29 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { int activeTexture = 0; glActiveTexture(GL_TEXTURE0 + activeTexture); + // State filtering tracking. int attrMask = 0; int colorMask = -1; int depthMask = -1; int depthFunc = -1; - // State filtering tracking. GLuint curArrayBuffer = (GLuint)-1; GLuint curElemArrayBuffer = (GLuint)-1; + bool depthEnabled = false; + bool blendEnabled = false; + bool cullEnabled = false; + bool ditherEnabled = false; + GLuint blendEqColor = (GLuint)-1; + GLuint blendEqAlpha = (GLuint)-1; auto &commands = step.commands; for (const auto &c : commands) { switch (c.cmd) { case GLRRenderCommand::DEPTH: if (c.depth.enabled) { - glEnable(GL_DEPTH_TEST); + if (!depthEnabled) { + glEnable(GL_DEPTH_TEST); + depthEnabled = true; + } if (c.depth.write != depthMask) { glDepthMask(c.depth.write); depthMask = c.depth.write; @@ -432,17 +442,38 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glDepthFunc(c.depth.func); depthFunc = c.depth.func; } - } else { + } else if (!c.depth.enabled && depthEnabled) { glDisable(GL_DEPTH_TEST); + depthEnabled = false; } break; + case GLRRenderCommand::STENCILFUNC: + if (c.stencilFunc.enabled) { + glEnable(GL_STENCIL_TEST); + glStencilFunc(c.stencilFunc.func, c.stencilFunc.ref, c.stencilFunc.compareMask); + } else { + glDisable(GL_STENCIL_TEST); + } + break; + case GLRRenderCommand::STENCILOP: + glStencilOp(c.stencilOp.sFail, c.stencilOp.zFail, c.stencilOp.pass); + glStencilMask(c.stencilOp.writeMask); + break; case GLRRenderCommand::BLEND: if (c.blend.enabled) { - glEnable(GL_BLEND); - glBlendEquationSeparate(c.blend.funcColor, c.blend.funcAlpha); + if (!blendEnabled) { + glEnable(GL_BLEND); + blendEnabled = true; + } + if (blendEqColor != c.blend.funcColor || blendEqAlpha != c.blend.funcAlpha) { + glBlendEquationSeparate(c.blend.funcColor, c.blend.funcAlpha); + blendEqColor = c.blend.funcColor; + blendEqAlpha = c.blend.funcAlpha; + } glBlendFuncSeparate(c.blend.srcColor, c.blend.dstColor, c.blend.srcAlpha, c.blend.dstAlpha); - } else { + } else if (!c.blend.enabled && blendEnabled) { glDisable(GL_BLEND); + blendEnabled = false; } if (c.blend.mask != colorMask) { glColorMask(c.blend.mask & 1, (c.blend.mask >> 1) & 1, (c.blend.mask >> 2) & 1, (c.blend.mask >> 3) & 1); @@ -569,18 +600,6 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } break; } - case GLRRenderCommand::STENCILFUNC: - if (c.stencilFunc.enabled) { - glEnable(GL_STENCIL_TEST); - glStencilFunc(c.stencilFunc.func, c.stencilFunc.ref, c.stencilFunc.compareMask); - } else { - glDisable(GL_STENCIL_TEST); - } - break; - case GLRRenderCommand::STENCILOP: - glStencilOp(c.stencilOp.sFail, c.stencilOp.zFail, c.stencilOp.pass); - glStencilMask(c.stencilOp.writeMask); - break; case GLRRenderCommand::BINDTEXTURE: { GLint slot = c.texture.slot; @@ -692,16 +711,24 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { break; case GLRRenderCommand::RASTER: if (c.raster.cullEnable) { - glEnable(GL_CULL_FACE); + if (!cullEnabled) { + glEnable(GL_CULL_FACE); + cullEnabled = true; + } glFrontFace(c.raster.frontFace); glCullFace(c.raster.cullFace); - } else { + } else if (!c.raster.cullEnable && cullEnabled) { glDisable(GL_CULL_FACE); + cullEnabled = false; } if (c.raster.ditherEnable) { - glEnable(GL_DITHER); - } else { + if (!ditherEnabled) { + glEnable(GL_DITHER); + ditherEnabled = true; + } + } else if (!c.raster.ditherEnable && ditherEnabled) { glDisable(GL_DITHER); + ditherEnabled = false; } break; default: @@ -858,7 +885,7 @@ void GLQueueRunner::fbo_ext_create(const GLRInitStep &step) { glGenRenderbuffersEXT(1, &fbo->z_stencil_buffer); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT, fbo->width, fbo->height); - //glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8, width, height); + // glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8, width, height); // Bind it all together glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->handle); From 2b12776137fbf36840e081eb48a10b1709f75c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 19 Dec 2017 12:59:57 +0100 Subject: [PATCH 26/96] Remove viewport flipping where it's not needed --- GPU/GLES/StateMappingGLES.cpp | 7 ------- ext/native/thin3d/GLQueueRunner.cpp | 4 ++-- ext/native/thin3d/GLQueueRunner.h | 1 - 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 932d148656..1c8f52911d 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -298,14 +298,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) { framebufferManager_->GetTargetBufferWidth(), framebufferManager_->GetTargetBufferHeight(), vpAndScissor); - if (!useBufferedRendering) { - vpAndScissor.scissorY = PSP_CoreParameter().pixelHeight - vpAndScissor.scissorH - vpAndScissor.scissorY; - } renderManager->SetScissor(GLRect2D{ vpAndScissor.scissorX, vpAndScissor.scissorY, vpAndScissor.scissorW, vpAndScissor.scissorH }); - - if (!useBufferedRendering) { - vpAndScissor.viewportY = PSP_CoreParameter().pixelHeight - vpAndScissor.viewportH - vpAndScissor.viewportY; - } renderManager->SetViewport({ vpAndScissor.viewportX, vpAndScissor.viewportY, vpAndScissor.viewportW, vpAndScissor.viewportH, diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 1d9f9ca108..c1230dbb54 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -521,7 +521,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { case GLRRenderCommand::VIEWPORT: { float y = c.viewport.vp.y; - if (!curFramebuffer_) + if (!curFB_) y = curFBHeight_ - y - c.viewport.vp.h; // TODO: Support FP viewports through glViewportArrays @@ -536,7 +536,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { case GLRRenderCommand::SCISSOR: { int y = c.scissor.rc.y; - if (!curFramebuffer_) + if (!curFB_) y = curFBHeight_ - y - c.scissor.rc.h; glScissor(c.scissor.rc.x, y, c.scissor.rc.w, c.scissor.rc.h); break; diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index 16a97f5be0..a2a4ae9dab 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -347,7 +347,6 @@ private: GLuint globalVAO_ = 0; - GLint curFramebuffer_ = 0; int curFBWidth_ = 0; int curFBHeight_ = 0; int targetWidth_ = 0; From 49c3cb83fe25396f43da88b92112242b845fe81c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 19 Dec 2017 14:35:24 +0100 Subject: [PATCH 27/96] GL render manager: Simple implementation of synchronous framebuffer readbacks. --- CMakeLists.txt | 6 +- ext/native/Android.mk | 3 + ext/native/native.vcxproj | 2 + ext/native/native.vcxproj.filters | 6 ++ ext/native/thin3d/DataFormatGL.cpp | 90 ++++++++++++++++++ ext/native/thin3d/DataFormatGL.h | 10 ++ ext/native/thin3d/GLQueueRunner.cpp | 54 ++++++++++- ext/native/thin3d/GLQueueRunner.h | 3 +- ext/native/thin3d/GLRenderManager.cpp | 67 +++++++++++++- ext/native/thin3d/thin3d_gl.cpp | 126 ++------------------------ 10 files changed, 246 insertions(+), 121 deletions(-) create mode 100644 ext/native/thin3d/DataFormatGL.cpp create mode 100644 ext/native/thin3d/DataFormatGL.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9aeeed988f..ee283b1079 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -832,7 +832,11 @@ endif() set(THIN3D_PLATFORMS ext/native/thin3d/thin3d_gl.cpp ext/native/thin3d/GLRenderManager.cpp - ext/native/thin3d/GLQueueRunner.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 diff --git a/ext/native/Android.mk b/ext/native/Android.mk index fd0e69181f..78a75db9d4 100644 --- a/ext/native/Android.mk +++ b/ext/native/Android.mk @@ -83,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 \ diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index a9929e396a..ebb1517ea2 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -240,6 +240,7 @@ + @@ -699,6 +700,7 @@ + diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index 7336d62265..e0507e183a 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -335,6 +335,9 @@ thin3d + + thin3d + @@ -805,6 +808,9 @@ thin3d + + thin3d + diff --git a/ext/native/thin3d/DataFormatGL.cpp b/ext/native/thin3d/DataFormatGL.cpp new file mode 100644 index 0000000000..389d6046e8 --- /dev/null +++ b/ext/native/thin3d/DataFormatGL.cpp @@ -0,0 +1,90 @@ +#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: + _assert_msg_(G3D, false, "Thin3d GL: Unsupported texture format %d", (int)fmt); + return false; + } + return true; +} + +} \ No newline at end of file diff --git a/ext/native/thin3d/DataFormatGL.h b/ext/native/thin3d/DataFormatGL.h new file mode 100644 index 0000000000..28b48f60ff --- /dev/null +++ b/ext/native/thin3d/DataFormatGL.h @@ -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); + +} diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index c1230dbb54..21d78f757d 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -1,5 +1,6 @@ #include "GLQueueRunner.h" #include "GLRenderManager.h" +#include "DataFormatGL.h" #include "base/logging.h" #include "gfx/gl_common.h" #include "gfx/gl_debug_log.h" @@ -789,6 +790,10 @@ void GLQueueRunner::PerformCopy(const GLRStep &step) { */ break; } + + _dbg_assert_(G3D, srcTex); + _dbg_assert_(G3D, dstTex); + #if defined(USING_GLES2) #ifndef IOS glCopyImageSubDataOES( @@ -813,7 +818,49 @@ void GLQueueRunner::PerformCopy(const GLRStep &step) { } void GLQueueRunner::PerformReadback(const GLRStep &pass) { + GLRFramebuffer *fb = pass.readback.src; + fbo_bind_fb_target(true, fb ? fb->handle : 0); + + // Reads from the "bound for read" framebuffer. + if (gl_extensions.GLES3 || !gl_extensions.IsGLES) + glReadBuffer(GL_COLOR_ATTACHMENT0); + + CHECK_GL_ERROR_IF_DEBUG(); + + GLuint internalFormat; + GLuint format; + GLuint type; + int alignment; + if (!Draw::Thin3DFormatToFormatAndType(pass.readback.dstFormat, internalFormat, format, type, alignment)) { + assert(false); + } + int pixelStride = pass.readback.srcRect.w; + // Apply the correct alignment. + glPixelStorei(GL_PACK_ALIGNMENT, alignment); + if (!gl_extensions.IsGLES || gl_extensions.GLES3) { + // Some drivers seem to require we specify this. See #8254. + glPixelStorei(GL_PACK_ROW_LENGTH, pixelStride); + } + + GLRect2D rect = pass.readback.srcRect; + + int size = alignment * rect.w * rect.h; + if (size > readbackBufferSize_) { + delete[] readbackBuffer_; + readbackBuffer_ = new uint8_t[size]; + readbackBufferSize_ = size; + } + + glReadPixels(rect.x, rect.y, rect.w, rect.h, format, type, readbackBuffer_); + + #ifdef DEBUG_READ_PIXELS + LogReadPixelsError(glGetError()); + #endif + if (!gl_extensions.IsGLES || gl_extensions.GLES3) { + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + } + CHECK_GL_ERROR_IF_DEBUG(); } void GLQueueRunner::PerformReadbackImage(const GLRStep &pass) { @@ -841,7 +888,12 @@ void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) { } void GLQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) { - + // TODO: Maybe move data format conversion here, and always read back 8888. Drivers + // don't usually provide very optimized implementations. + int bpp = Draw::DataFormatSizeInBytes(destFormat); + for (int y = 0; y < height; y++) { + memcpy(pixels + y * pixelStride * bpp, readbackBuffer_ + y * width * bpp, pixelStride * bpp); + } } GLuint GLQueueRunner::AllocTextureName() { diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index a2a4ae9dab..6bea398557 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -289,6 +289,7 @@ struct GLRStep { int aspectMask; GLRFramebuffer *src; GLRect2D srcRect; + Draw::DataFormat dstFormat; } readback; struct { GLint texture; @@ -354,7 +355,7 @@ private: // Readback buffer. Currently we only support synchronous readback, so we only really need one. // We size it generously. - GLint readbackBuffer_ = 0; + uint8_t *readbackBuffer_ = nullptr; int readbackBufferSize_ = 0; float maxAnisotropyLevel_ = 0.0f; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 2ccccd5adb..27b9fe6ad3 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -215,6 +215,33 @@ void GLRenderManager::BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLR steps_.push_back(step); } +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); + + 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::BeginFrame() { VLOG("BeginFrame"); @@ -255,14 +282,18 @@ void GLRenderManager::Finish() { 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 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(); @@ -340,6 +371,41 @@ void GLRenderManager::Run(int frame) { VLOG("PULL: Finished running frame %d", frame); } +void GLRenderManager::FlushSync() { + // 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 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 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; + } +} + void GLRenderManager::EndSyncFrame(int frame) { FrameData &frameData = frameData_[frame]; Submit(frame, false); @@ -347,7 +413,6 @@ void GLRenderManager::EndSyncFrame(int frame) { // This is brutal! Should probably wait for a fence instead, not that it'll matter much since we'll // still stall everything. glFinish(); - // vkDeviceWaitIdle(vulkan_->GetDevice()); // 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. diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index f65b765e48..194d065ec0 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -9,6 +9,7 @@ #include "math/dataconv.h" #include "math/lin/matrix4x4.h" #include "thin3d/thin3d.h" +#include "thin3d/DataFormatGL.h" #include "gfx/gl_common.h" #include "gfx/gl_debug_log.h" #include "gfx_es2/gpu_features.h" @@ -662,90 +663,6 @@ public: FBColorDepth colorDepth; }; - -// TODO: Also output storage format (GL_RGBA8 etc) for modern GL usage. -static 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: - ELOG("Thin3d GL: Unsupported texture format %d", (int)fmt); - return false; - } - return true; -} - void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { if (width != width_ || height != height_ || depth != depth_) { // When switching to texStorage we need to handle this correctly. @@ -811,39 +728,14 @@ static void LogReadPixelsError(GLenum error) { bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels, int pixelStride) { OpenGLFramebuffer *fb = (OpenGLFramebuffer *)src; - /* - fbo_bind_fb_target(true, fb ? fb->handle : 0); - - // Reads from the "bound for read" framebuffer. - if (gl_extensions.GLES3 || !gl_extensions.IsGLES) - glReadBuffer(GL_COLOR_ATTACHMENT0); - - CHECK_GL_ERROR_IF_DEBUG(); - - GLuint internalFormat; - GLuint format; - GLuint type; - int alignment; - if (!Thin3DFormatToFormatAndType(dataFormat, internalFormat, format, type, alignment)) { - assert(false); - } - // Apply the correct alignment. - glPixelStorei(GL_PACK_ALIGNMENT, alignment); - if (!gl_extensions.IsGLES || gl_extensions.GLES3) { - // Even if not required, some drivers seem to require we specify this. See #8254. - glPixelStorei(GL_PACK_ROW_LENGTH, pixelStride); - } - - glReadPixels(x, y, w, h, format, type, pixels); -#ifdef DEBUG_READ_PIXELS - LogReadPixelsError(glGetError()); -#endif - - if (!gl_extensions.IsGLES || gl_extensions.GLES3) { - glPixelStorei(GL_PACK_ROW_LENGTH, 0); - } - CHECK_GL_ERROR_IF_DEBUG(); - */ + GLuint aspect = 0; + if (channelBits & FB_COLOR_BIT) + aspect |= GL_COLOR_BUFFER_BIT; + if (channelBits & FB_DEPTH_BIT) + aspect |= GL_DEPTH_BUFFER_BIT; + if (channelBits & FB_STENCIL_BIT) + aspect |= GL_STENCIL_BUFFER_BIT; + renderManager_.CopyFramebufferToMemorySync(fb->framebuffer, aspect, x, y, w, h, dataFormat, (uint8_t *)pixels, pixelStride); return true; } From 42f2312030ac58526f804892554203691dacb3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 15 Dec 2017 12:40:38 +0100 Subject: [PATCH 28/96] Remove the old CPU threading remains, start redesigning interfaces. --- Common/GraphicsContext.h | 8 ++ Core/Core.h | 4 + Core/System.cpp | 172 +--------------------------- Core/System.h | 2 - Windows/EmuThread.cpp | 35 +++++- Windows/EmuThread.h | 2 +- Windows/GPU/WindowsGLContext.h | 2 +- Windows/MainWindow.cpp | 2 +- Windows/main.cpp | 4 +- ext/native/thin3d/GLQueueRunner.cpp | 4 +- 10 files changed, 56 insertions(+), 179 deletions(-) diff --git a/Common/GraphicsContext.h b/Common/GraphicsContext.h index c8495b04be..e32dd6c306 100644 --- a/Common/GraphicsContext.h +++ b/Common/GraphicsContext.h @@ -10,6 +10,11 @@ class GraphicsContext { public: virtual ~GraphicsContext() {} + // Threaded backends (that need to do init on the final render thread, like GL) + // call this from the render thread. Init() should block until InitFromThread is done. + // Other backends can ignore this. + virtual bool InitFromThread() { return true; } + virtual void Shutdown() = 0; virtual void SwapInterval(int interval) = 0; @@ -25,6 +30,9 @@ 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 ThreadFrame() {} + virtual Draw::DrawContext *GetDrawContext() = 0; }; diff --git a/Core/Core.h b/Core/Core.h index 98262f7beb..9adbd6356e 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -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(); diff --git a/Core/System.cpp b/Core/System.cpp index cc41c39313..772f00e877 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -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 guard(cpuThreadLock); - CPU_SetStateNoLock(to); -} - -bool CPU_NextState(CPUThreadState from, CPUThreadState to) { - std::lock_guard guard(cpuThreadLock); - if (cpuThreadState == from) { - CPU_SetStateNoLock(to); - return true; - } else { - return false; - } -} - -bool CPU_NextStateNot(CPUThreadState from, CPUThreadState to) { - std::lock_guard 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 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(); } diff --git a/Core/System.h b/Core/System.h index 9cc4cfdc89..6017e88cc5 100644 --- a/Core/System.h +++ b/Core/System.h @@ -78,8 +78,6 @@ void Core_UpdateDebugStats(bool collectStats); void Audio_Init(); void Audio_Shutdown(); - -bool IsOnSeparateCPUThread(); bool IsAudioInitialised(); void UpdateLoadedFile(FileLoader *fileLoader); diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 87ae5f1d17..2617ed00c4 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -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,18 @@ static std::mutex emuThreadLock; static std::thread emuThread; static std::atomic emuThreadState; +static std::mutex renderThreadLock; +static std::thread renderThread; +static std::atomic renderThreadReady; + +static bool useRenderThread; + extern std::vector GetWideCmdLine(); +class GraphicsContext; + +static GraphicsContext *g_graphicsContext; + enum EmuThreadStatus : int { THREAD_NONE = 0, THREAD_INIT, @@ -38,10 +49,15 @@ enum EmuThreadStatus : int { }; void EmuThreadFunc(); +void RenderThreadFunc(); -void EmuThread_Start() { +void EmuThread_Start(bool separateRenderThread) { std::lock_guard guard(emuThreadLock); emuThread = std::thread(&EmuThreadFunc); + useRenderThread = separateRenderThread; + if (useRenderThread) { + renderThread = std::thread(&RenderThreadFunc); + } } void EmuThread_Stop() { @@ -56,6 +72,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 +83,19 @@ bool EmuThread_Ready() { return emuThreadState == THREAD_CORE_LOOP; } +void RenderThreadFunc() { + setCurrentThreadName("Render"); + while (!g_graphicsContext) { + sleep_ms(50); + continue; + } + g_graphicsContext->InitFromThread(); + while (true) { + g_graphicsContext->ThreadFrame(); + break; + } +} + void EmuThreadFunc() { emuThreadState = THREAD_INIT; @@ -175,4 +207,3 @@ shutdown: emuThreadState = THREAD_END; } - diff --git a/Windows/EmuThread.h b/Windows/EmuThread.h index ed1a9df08d..d232fefb14 100644 --- a/Windows/EmuThread.h +++ b/Windows/EmuThread.h @@ -17,6 +17,6 @@ #pragma once -void EmuThread_Start(); +void EmuThread_Start(bool separateRenderThread); void EmuThread_Stop(); bool EmuThread_Ready(); diff --git a/Windows/GPU/WindowsGLContext.h b/Windows/GPU/WindowsGLContext.h index 3fe2f9f4a8..4ddd28ba61 100644 --- a/Windows/GPU/WindowsGLContext.h +++ b/Windows/GPU/WindowsGLContext.h @@ -10,6 +10,7 @@ namespace Draw { class WindowsGLContext : public WindowsGraphicsContext { public: bool Init(HINSTANCE hInst, HWND window, std::string *error_message) override; + void Shutdown() override; void SwapInterval(int interval) override; void SwapBuffers() override; @@ -18,7 +19,6 @@ public: // not the rendering thread or CPU thread. void Pause() override; void Resume() override; - void Resize() override; Draw::DrawContext *GetDrawContext() override { return draw_; } diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index f56ade7bce..a3c45e3567 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -917,7 +917,7 @@ namespace MainWindow EmuThread_Stop(); coreState = CORE_POWERUP; ResetUIState(); - EmuThread_Start(); + EmuThread_Start(false); InputDevice::BeginPolling(); break; diff --git a/Windows/main.cpp b/Windows/main.cpp index 4d6a28c849..5bbe6ec01a 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -529,8 +529,8 @@ 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! + EmuThread_Start(false); // g_Config.iGPUBackend == GPU_BACKEND_VULKAN); InputDevice::BeginPolling(); HACCEL hAccelTable = LoadAccelerators(_hInstance, (LPCTSTR)IDR_ACCELS); diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 21d78f757d..1e3597fdb5 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -889,8 +889,8 @@ void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) { void GLQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) { // TODO: Maybe move data format conversion here, and always read back 8888. Drivers - // don't usually provide very optimized implementations. - int bpp = Draw::DataFormatSizeInBytes(destFormat); + // don't usually provide very optimized conversion implementations, though some do. + int bpp = (int)Draw::DataFormatSizeInBytes(destFormat); for (int y = 0; y < height; y++) { memcpy(pixels + y * pixelStride * bpp, readbackBuffer_ + y * width * bpp, pixelStride * bpp); } From 63b9140ebfe85de5bbfa9ff4acbfde8c483ea568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 21 Dec 2017 15:38:55 +0100 Subject: [PATCH 29/96] Rip out async readbacks from FramebufferManagerGLES. They should be implemented in GLQueueRunner, differently. --- GPU/Common/FramebufferCommon.h | 2 +- GPU/GLES/FramebufferManagerGLES.cpp | 161 +--------------------------- GPU/GLES/FramebufferManagerGLES.h | 41 ++----- 3 files changed, 13 insertions(+), 191 deletions(-) diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 8545115e86..9cf359bfa9 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -224,7 +224,7 @@ public: virtual 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); diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index dbfa48c1ad..9902871089 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -238,18 +238,7 @@ void FramebufferManagerGLES::BindPostShader(const PostShaderUniforms &uniforms) FramebufferManagerGLES::FramebufferManagerGLES(Draw::DrawContext *draw, GLRenderManager *render) : FramebufferManagerCommon(draw), - render_(render), - 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; @@ -315,14 +304,6 @@ void FramebufferManagerGLES::DestroyDeviceObjects() { FramebufferManagerGLES::~FramebufferManagerGLES() { DestroyDeviceObjects(); - if (pixelBufObj_) { - for (int i = 0; i < MAX_PBO; i++) { - if (pixelBufObj_[i].buffer) { - render_->DeleteBuffer(pixelBufObj_[i].buffer); - } - } - delete[] pixelBufObj_; - } delete [] convBuf_; } @@ -540,46 +521,19 @@ 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); - } - } - } + 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)) { @@ -758,117 +712,6 @@ void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u } } -void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { - 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; - } - - pixelBufObj_ = new AsyncPBO[MAX_PBO]{}; - } - - // Receive previously requested data from a PBO - AsyncPBO &pbo = pixelBufObj_[nextPBO]; - if (pbo.reading) { - render_->BindPixelPackBuffer(pbo.buffer); -#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; - } - - if (pixelBufObj_[currentPBO_].maxSize < bufSize) { - if (pixelBufObj_[currentPBO_].buffer) { - render_->DeleteBuffer(pixelBufObj_[currentPBO_].buffer); - } - pixelBufObj_[currentPBO_].buffer = render_->CreateBuffer(GL_PIXEL_PACK_BUFFER, bufSize, GL_DYNAMIC_READ); - pixelBufObj_[currentPBO_].maxSize = bufSize; - } - - render_->BindPixelPackBuffer(pixelBufObj_[currentPBO_].buffer); - // 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) { - render_->BindPixelPackBuffer(0); - } -} - 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"); diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index 60c5c841ad..969194babb 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -36,19 +36,6 @@ class TextureCacheGLES; class DrawEngineGLES; class ShaderManagerGLES; -// Simple struct for asynchronous PBO readbacks -struct AsyncPBO { - GLRBuffer *buffer; - u32 maxSize; - - u32 fb_address; - u32 stride; - u32 height; - u32 size; - GEBufferFormat format; - bool reading; -}; - class FramebufferManagerGLES : public FramebufferManagerCommon { public: FramebufferManagerGLES(Draw::DrawContext *draw, GLRenderManager *render); @@ -67,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; @@ -77,7 +63,6 @@ public: // 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; @@ -105,24 +90,22 @@ private: void CompileDraw2DProgram(); 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 - GLRTexture *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_; + u8 *convBuf_ = nullptr; + u32 convBufSize_ = 0; GLRProgram *draw2dprogram_ = nullptr; GLRProgram *postShaderProgram_ = nullptr; - GLRProgram *stencilUploadProgram_ = nullptr; int u_stencilUploadTex = -1; int u_stencilValue = -1; @@ -137,17 +120,13 @@ private: int pixelDeltaLoc_ = -1; int deltaLoc_ = -1; - TextureCacheGLES *textureCacheGL_; - ShaderManagerGLES *shaderManagerGL_; - DrawEngineGLES *drawEngineGL_; + TextureCacheGLES *textureCacheGL_ = nullptr; + ShaderManagerGLES *shaderManagerGL_ = nullptr; + DrawEngineGLES *drawEngineGL_ = nullptr; struct Simple2DVertex { float pos[3]; float uv[2]; }; - GLRInputLayout *simple2DInputLayout_; - - // Not used under ES currently. - AsyncPBO *pixelBufObj_; //this isn't that large - u8 currentPBO_; + GLRInputLayout *simple2DInputLayout_ = nullptr; }; From 542f9f9ef1f095d9d2b498b49b90d74539817071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 21 Dec 2017 19:05:33 +0100 Subject: [PATCH 30/96] Assorted cleanup --- GPU/Common/FramebufferCommon.h | 2 +- GPU/GLES/DrawEngineGLES.cpp | 2 +- GPU/GLES/FramebufferManagerGLES.cpp | 68 --------------------------- GPU/GLES/FramebufferManagerGLES.h | 4 -- Windows/GPU/WindowsGLContext.cpp | 35 ++++++-------- ext/native/thin3d/GLQueueRunner.cpp | 5 -- ext/native/thin3d/GLRenderManager.cpp | 7 +++ 7 files changed, 23 insertions(+), 100 deletions(-) diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 9cf359bfa9..9ab59a4b0b 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -222,7 +222,7 @@ 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); void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes); void DrawFramebufferToOutput(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, bool applyPostShader); diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 1cf8e67af5..225ad05ce7 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -156,7 +156,7 @@ void DrawEngineGLES::DeviceRestore() { void DrawEngineGLES::InitDeviceObjects() { 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, 512 * 1024); + frameData_[i].pushIndex = new GLPushBuffer(render_, GL_ELEMENT_ARRAY_BUFFER, 256 * 1024); } int vertexSize = sizeof(TransformedVertex); diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 9902871089..7e117d3dda 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -519,21 +519,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 (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_(nvfb, x, y, w, h); - - textureCacheGL_->ForgetLastTexture(); - RebindFramebuffer(); - } -} - bool FramebufferManagerGLES::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { // When updating VRAM, it need to be exact format. if (!gstate_c.Supports(GPU_PREFER_CPU_DOWNLOAD)) { @@ -568,8 +553,6 @@ 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 }); } @@ -712,57 +695,6 @@ void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u } } -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); - } - } -} - 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"); diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index 969194babb..c3f0594637 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -61,9 +61,6 @@ 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; - bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; @@ -90,7 +87,6 @@ private: void CompileDraw2DProgram(); void CompilePostShader(); - 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_; diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index 283797b064..100814f5b0 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -35,7 +35,6 @@ void WindowsGLContext::SwapBuffers() { ::SwapBuffers(hDC); - // Used during fullscreen switching to prevent rendering. if (pauseRequested) { SetEvent(pauseEvent); @@ -46,10 +45,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 +91,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 +103,27 @@ 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]\n", msg, sourceStr, typeStr, severityStr, id); + snprintf(severityStr, sizeof(severityStr), severityFmt, severity); + snprintf(outStr, sizeof(outStr), "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; + FILE *outFile = (FILE *)userParam; char finalMessage[256]; FormatDebugOutputARB(finalMessage, 256, source, type, id, severity, message); OutputDebugStringA(finalMessage); - switch (type) { case GL_DEBUG_TYPE_ERROR_ARB: case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: @@ -153,6 +145,7 @@ void DebugCallbackARB(GLenum source, GLenum type, GLuint id, GLenum severity, bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_message) { glslang::InitializeProcess(); + *error_message = "ok"; hWnd = window; GLuint PixelFormat; @@ -186,7 +179,7 @@ bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_mes 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 +189,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 +218,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); } @@ -383,12 +376,12 @@ 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; } @@ -396,7 +389,7 @@ void WindowsGLContext::Shutdown() { 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; } diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 1e3597fdb5..8e02ffe1bd 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -919,11 +919,6 @@ void GLQueueRunner::fbo_ext_create(const GLRInitStep &step) { // Create the surfaces. glBindTexture(GL_TEXTURE_2D, fbo->color_texture); - 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_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 27b9fe6ad3..c78dc26a66 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -15,21 +15,27 @@ 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() { @@ -46,6 +52,7 @@ GLRenderManager::~GLRenderManager() { for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { } + if (!useThread_) { queueRunner_.DestroyDeviceObjects(); } From af8e825578ea1b958c296bebb8f6c1220586482d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 27 Dec 2017 12:44:46 +0100 Subject: [PATCH 31/96] Fix terrible drawing glitches when we do sync readbacks. --- GPU/GLES/DrawEngineGLES.cpp | 5 +++++ ext/native/thin3d/GLRenderManager.cpp | 15 ++++++++++++++- ext/native/thin3d/GLRenderManager.h | 16 ++++++++++++++++ ext/native/thin3d/thin3d_gl.cpp | 2 ++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 225ad05ce7..d6d803ed12 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -157,6 +157,9 @@ void DrawEngineGLES::InitDeviceObjects() { 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); + + render_->RegisterPushBuffer(i, frameData_[i].pushVertex); + render_->RegisterPushBuffer(i, frameData_[i].pushIndex); } int vertexSize = sizeof(TransformedVertex); @@ -170,6 +173,8 @@ void DrawEngineGLES::InitDeviceObjects() { void DrawEngineGLES::DestroyDeviceObjects() { for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) { + render_->UnregisterPushBuffer(i, frameData_[i].pushVertex); + render_->UnregisterPushBuffer(i, frameData_[i].pushIndex); frameData_[i].pushVertex->Destroy(); frameData_[i].pushIndex->Destroy(); delete frameData_[i].pushVertex; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index c78dc26a66..452497d3e2 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -379,6 +379,11 @@ void GLRenderManager::Run(int 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]; @@ -465,6 +470,14 @@ void GLPushBuffer::Unmap() { 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_]; @@ -527,4 +540,4 @@ size_t GLPushBuffer::GetTotalSize() const { sum += size_ * (buffers_.size() - 1); sum += offset_; return sum; -} +} \ No newline at end of file diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index de559fcc31..59d753537e 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,7 @@ #include "GLQueueRunner.h" class GLRInputLayout; +class GLPushBuffer; class GLRFramebuffer { public: @@ -596,6 +598,17 @@ public: 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); + } + private: void BeginSubmitFrame(int frame); void EndSubmitFrame(int frame); @@ -629,6 +642,7 @@ private: uint32_t curSwapchainImage = -1; GLDeleter deleter; + std::set activePushBuffers; }; FrameData frameData_[MAX_INFLIGHT_FRAMES]; @@ -749,6 +763,8 @@ public: size_t GetTotalSize() const; + void Flush(); + private: bool AddBuffer(); void NextBuffer(size_t minSize); diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 194d065ec0..cf05ae4b75 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -526,11 +526,13 @@ OpenGLContext::OpenGLContext() { } for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) { frameData_[i].push = new GLPushBuffer(&renderManager_, GL_ARRAY_BUFFER, 64 * 1024); + renderManager_.RegisterPushBuffer(i, frameData_[i].push); } } OpenGLContext::~OpenGLContext() { for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) { + renderManager_.UnregisterPushBuffer(i, frameData_[i].push); frameData_[i].push->Destroy(); delete frameData_[i].push; } From 43ebbbcdb6c210e699eb4f51ed759e2eece2a629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 27 Dec 2017 13:03:04 +0100 Subject: [PATCH 32/96] More state dirtying, a comment --- GPU/Common/FramebufferCommon.cpp | 5 ++--- ext/native/thin3d/GLRenderManager.h | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 7425c88761..bb161849b3 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1930,9 +1930,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; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 59d753537e..e712482ddf 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -113,6 +113,7 @@ public: } GLuint texture; GLenum target; + // TODO: Move sampler params here so we can move the dirty-tracking from TextureCacheGLES to here. }; class GLRBuffer { From 5c7c7ce1926547ab96f2c6afb60561968f834582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 27 Dec 2017 14:33:18 +0100 Subject: [PATCH 33/96] Move GL sampler state tracking into GLRenderManager/QueueRunner. --- GPU/Common/FramebufferCommon.cpp | 2 + GPU/Common/TextureCacheCommon.h | 8 ---- GPU/Directx9/TextureCacheDX9.cpp | 1 - GPU/GLES/TextureCacheGLES.cpp | 13 ++---- ext/native/thin3d/GLQueueRunner.cpp | 72 ++++++++++++++++++++++------- ext/native/thin3d/GLRenderManager.h | 14 ++++-- 6 files changed, 72 insertions(+), 38 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index bb161849b3..c0d747802f 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1965,6 +1965,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; diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index aa69f123a5..1bc16a2541 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -148,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); } diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 123eebd0ff..53e69baa56 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -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); diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 3ff8704bca..8560438e04 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -130,7 +130,7 @@ void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) { 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) { minLod = 0.0f; maxLod = (float)maxLevel; @@ -147,7 +147,6 @@ void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) { minLod = 0.0f; maxLod = (float)maxLevel; } - entry.lodBias = lodBias; } } else { minLod = 0.0f; @@ -156,14 +155,8 @@ void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) { render_->SetTextureLod(minLod, maxLod, lodBias); } - if (force || entry.minFilt != minFilt || entry.magFilt != magFilt || entry.sClamp != sClamp || entry.tClamp != tClamp) { - 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); - entry.minFilt = minFilt; - entry.magFilt = magFilt; - entry.sClamp = sClamp; - entry.tClamp = tClamp; - } + 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); CHECK_GL_ERROR_IF_DEBUG(); } diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 8e02ffe1bd..872d7b03dd 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -409,8 +409,8 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { GLRFramebuffer *fb = step.render.framebuffer; GLRProgram *curProgram = nullptr; - int activeTexture = 0; - glActiveTexture(GL_TEXTURE0 + activeTexture); + int activeSlot = 0; + glActiveTexture(GL_TEXTURE0 + activeSlot); // State filtering tracking. int attrMask = 0; @@ -426,6 +426,8 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { GLuint blendEqColor = (GLuint)-1; GLuint blendEqAlpha = (GLuint)-1; + GLRTexture *curTex[8]{}; + auto &commands = step.commands; for (const auto &c : commands) { switch (c.cmd) { @@ -604,23 +606,27 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { case GLRRenderCommand::BINDTEXTURE: { GLint slot = c.texture.slot; - if (slot != activeTexture) { + if (slot != activeSlot) { glActiveTexture(GL_TEXTURE0 + slot); - activeTexture = slot; + activeSlot = slot; } if (c.texture.texture) { - glBindTexture(c.texture.texture->target, c.texture.texture->texture); + if (curTex[slot] != c.texture.texture) { + glBindTexture(c.texture.texture->target, c.texture.texture->texture); + curTex[slot] = c.texture.texture; + } } else { glBindTexture(GL_TEXTURE_2D, 0); // Which target? Well we only use this one anyway... + curTex[slot] = nullptr; } break; } case GLRRenderCommand::BIND_FB_TEXTURE: { GLint slot = c.bind_fb_texture.slot; - if (slot != activeTexture) { + if (slot != activeSlot) { glActiveTexture(GL_TEXTURE0 + slot); - activeTexture = slot; + activeSlot = slot; } if (c.bind_fb_texture.aspect == GL_COLOR_BUFFER_BIT) { glBindTexture(GL_TEXTURE_2D, c.bind_fb_texture.framebuffer->color_texture); @@ -695,21 +701,55 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } break; case GLRRenderCommand::TEXTURESAMPLER: - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, c.textureSampler.wrapS); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c.textureSampler.wrapT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, c.textureSampler.magFilter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c.textureSampler.minFilter); - if (c.textureSampler.anisotropy != 0.0f) { + { + GLRTexture *tex = curTex[activeSlot]; + if (!tex) { + break; + } + if (tex->wrapS != c.textureSampler.wrapS) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, c.textureSampler.wrapS); + tex->wrapS = c.textureSampler.wrapS; + } + if (tex->wrapT != c.textureSampler.wrapT) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c.textureSampler.wrapT); + tex->wrapT = c.textureSampler.wrapT; + } + if (tex->magFilter != c.textureSampler.magFilter) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, c.textureSampler.magFilter); + tex->magFilter = c.textureSampler.magFilter; + } + if (tex->minFilter != c.textureSampler.minFilter) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c.textureSampler.minFilter); + tex->minFilter = c.textureSampler.minFilter; + } + if (tex->anisotropy != c.textureSampler.anisotropy) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, c.textureSampler.anisotropy); + tex->anisotropy = c.textureSampler.anisotropy; } break; + } case GLRRenderCommand::TEXTURELOD: - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, c.textureLod.minLod); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, c.textureLod.maxLod); + { + GLRTexture *tex = curTex[activeSlot]; + if (!tex) { + break; + } #ifndef USING_GLES2 - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, c.textureLod.lodBias); + if (tex->lodBias != c.textureLod.lodBias) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, c.textureLod.lodBias); + tex->lodBias = c.textureLod.lodBias; + } #endif + if (tex->minLod != c.textureLod.minLod) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, c.textureLod.minLod); + tex->minLod = c.textureLod.minLod; + } + if (tex->maxLod != c.textureLod.maxLod) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, c.textureLod.maxLod); + tex->maxLod = c.textureLod.maxLod; + } break; + } case GLRRenderCommand::RASTER: if (c.raster.cullEnable) { if (!cullEnabled) { @@ -744,7 +784,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } } - if (activeTexture != 0) + if (activeSlot != 0) glActiveTexture(GL_TEXTURE0); // Wipe out the current state. diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index e712482ddf..6d0e44f0ca 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -111,9 +111,17 @@ public: glDeleteTextures(1, &texture); } } - GLuint texture; - GLenum target; - // TODO: Move sampler params here so we can move the dirty-tracking from TextureCacheGLES to here. + 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 GLRBuffer { From 7d6f9aec4d95f0eab2c37559a532dea0fd60c82d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 27 Dec 2017 14:46:01 +0100 Subject: [PATCH 34/96] iOS buildfix --- ext/native/thin3d/GLQueueRunner.cpp | 4 ++++ ext/native/thin3d/thin3d_gl.cpp | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 872d7b03dd..45e14ad164 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -9,6 +9,10 @@ #define TEXCACHE_NAME_CACHE_SIZE 16 +#ifdef IOS +extern void bindDefaultFBO(); +#endif + // Workaround for Retroarch. Simply declare // extern GLuint g_defaultFBO; // and set is as appropriate. Can adjust the variables in ext/native/base/display.h as diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index cf05ae4b75..22721ed468 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -16,10 +16,6 @@ #include "thin3d/GLRenderManager.h" -#ifdef IOS -extern void bindDefaultFBO(); -#endif - // #define DEBUG_READ_PIXELS 1 namespace Draw { From 422b05e51fd0a9742fe14c2e38819a232bdd41e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 10 Jan 2018 09:45:05 +0100 Subject: [PATCH 35/96] Truncate the '\n' from GL debug strings before logging them. --- Windows/GPU/WindowsGLContext.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index 100814f5b0..b4c0692246 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -121,9 +121,16 @@ 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); + 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: From 2656ad7d40d1c36254f08809dc570a0d1d43c521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 10 Jan 2018 10:17:42 +0100 Subject: [PATCH 36/96] Vulkan: Use push_back instead of resize(+1) --- Common/Vulkan/VulkanMemory.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Common/Vulkan/VulkanMemory.cpp b/Common/Vulkan/VulkanMemory.cpp index 721b97e5b5..1c4bfa68fb 100644 --- a/Common/Vulkan/VulkanMemory.cpp +++ b/Common/Vulkan/VulkanMemory.cpp @@ -75,9 +75,8 @@ bool VulkanPushBuffer::AddBuffer() { return false; } - buf_ = buffers_.size(); - buffers_.resize(buf_ + 1); // Why not push_back? - buffers_[buf_] = info; + buffers_.push_back(info); + buf_ = buffers_.size() - 1; return true; } From 465939e1c813163ccc3265a7a6534a3e24eb3f7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 10 Jan 2018 10:18:18 +0100 Subject: [PATCH 37/96] Minor fixes, indentation and comments --- Windows/EmuThread.cpp | 6 +++--- ext/native/thin3d/GLQueueRunner.cpp | 8 ++++---- ext/native/thin3d/GLRenderManager.h | 4 ++-- ext/native/thin3d/thin3d_gl.cpp | 5 +++-- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 2617ed00c4..36dd44c34d 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -72,9 +72,9 @@ void EmuThread_Stop() { Core_Stop(); Core_WaitInactive(800); emuThread.join(); - if (useRenderThread) { - renderThread.join(); - } + if (useRenderThread) { + renderThread.join(); + } PostMessage(MainWindow::GetHWND(), MainWindow::WM_USER_UPDATE_UI, 0, 0); } diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 45e14ad164..fd8286511a 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -134,13 +134,12 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } } } - } break; + } case GLRInitStepType::CREATE_SHADER: { GLuint shader = glCreateShader(step.create_shader.stage); step.create_shader.shader->shader = shader; - // language_ = language; const char *code = step.create_shader.code; glShaderSource(shader, 1, &code, nullptr); delete[] code; @@ -667,7 +666,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } } attrMask = layout->semanticsMask_; - for (int i = 0; i < layout->entries.size(); i++) { + for (size_t i = 0; i < layout->entries.size(); i++) { auto &entry = layout->entries[i]; glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, entry.stride, (const void *)(c.bindVertexBuffer.offset + entry.offset)); } @@ -826,6 +825,7 @@ void GLQueueRunner::PerformCopy(const GLRStep &step) { dstTex = dst->color_texture; break; case GL_DEPTH_BUFFER_BIT: + // TODO: Support depth copies. _assert_msg_(G3D, false, "Depth copies not yet supported - soon"); target = GL_RENDERBUFFER; /* @@ -1101,4 +1101,4 @@ GLRFramebuffer::~GLRFramebuffer() { } glDeleteTextures(1, &color_texture); -} \ No newline at end of file +} diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 6d0e44f0ca..22e5679258 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include #include @@ -101,7 +101,7 @@ public: } return loc; } - std::map uniformCache_; + std::unordered_map uniformCache_; }; class GLRTexture { diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 22721ed468..83ae2c484f 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -237,7 +237,7 @@ public: ~OpenGLShaderModule() { if (shader_) - render_->DeleteShader(shader_); + render_->DeleteShader(shader_); } bool Compile(GLRenderManager *render, ShaderLanguage language, const uint8_t *data, size_t dataSize); @@ -1036,7 +1036,8 @@ OpenGLInputLayout::~OpenGLInputLayout() { void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) { int semMask = 0; - // This is only accurate if there's only one stream. But whatever, for now. + // TODO: This is only accurate if there's only one stream. But whatever, for now we + // never use multiple streams anyway. stride = (GLsizei)desc.bindings[0].stride; std::vector entries; From b7f4f6e157f4b9b388c3be3916961b48c7b6d9f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 10 Jan 2018 10:48:46 +0100 Subject: [PATCH 38/96] GL render manager: Improve shader error reporting. --- GPU/GLES/DepalettizeShaderGLES.cpp | 4 ++-- GPU/GLES/FramebufferManagerGLES.cpp | 8 ++++---- GPU/GLES/ShaderManagerGLES.cpp | 13 ++++++++----- GPU/GLES/ShaderManagerGLES.h | 4 ++-- GPU/GLES/StencilBufferGLES.cpp | 4 ++-- ext/native/thin3d/GLQueueRunner.cpp | 18 ++++++++++++++---- ext/native/thin3d/GLQueueRunner.h | 2 ++ ext/native/thin3d/GLRenderManager.h | 6 +++++- ext/native/thin3d/thin3d_gl.cpp | 2 +- 9 files changed, 40 insertions(+), 21 deletions(-) diff --git a/GPU/GLES/DepalettizeShaderGLES.cpp b/GPU/GLES/DepalettizeShaderGLES.cpp index 95f58f37c6..bc3a84297f 100644 --- a/GPU/GLES/DepalettizeShaderGLES.cpp +++ b/GPU/GLES/DepalettizeShaderGLES.cpp @@ -72,7 +72,7 @@ DepalShaderCacheGLES::~DepalShaderCacheGLES() { bool DepalShaderCacheGLES::CreateVertexShader() { std::string src(useGL3_ ? depalVShader300 : depalVShader100); - vertexShader_ = render_->CreateShader(GL_VERTEX_SHADER, src); + vertexShader_ = render_->CreateShader(GL_VERTEX_SHADER, src, "depal"); return true; } @@ -154,7 +154,7 @@ DepalShader *DepalShaderCacheGLES::GetDepalettizeShader(uint32_t clutMode, GEBuf GenerateDepalShader(buffer, pixelFormat, useGL3_ ? GLSL_300 : GLSL_140); std::string src(buffer); - GLRShader *fragShader = render_->CreateShader(GL_FRAGMENT_SHADER, src); + GLRShader *fragShader = render_->CreateShader(GL_FRAGMENT_SHADER, src, "depal"); DepalShader *depal = new DepalShader(); diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 7e117d3dda..67d9ce44cb 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -91,8 +91,8 @@ void FramebufferManagerGLES::CompileDraw2DProgram() { vs_code = ApplyGLSLPrelude(basic_vs, GL_VERTEX_SHADER); fs_code = ApplyGLSLPrelude(tex_fs, GL_FRAGMENT_SHADER); std::vector shaders; - shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vs_code)); - shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fs_code)); + shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vs_code, "draw2d")); + shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fs_code, "draw2d")); std::vector queries; queries.push_back({ &u_draw2d_tex, "u_tex" }); @@ -153,8 +153,8 @@ void FramebufferManagerGLES::CompilePostShader() { SetNumExtraFBOs(1); std::vector shaders; - shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vshader)); - shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, fshader)); + shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vshader, "postshader")); + shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, fshader, "postshader")); std::vector queries; queries.push_back({ &u_postShaderTex, "tex" }); queries.push_back({ &deltaLoc_, "u_texelDelta" }); diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 22d21f50d6..4c10e61527 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -44,11 +44,12 @@ #include "GPU/GLES/DrawEngineGLES.h" #include "FramebufferManagerGLES.h" -Shader::Shader(GLRenderManager *render, const char *code, uint32_t glShaderType, bool useHWTransform, uint32_t attrMask, uint64_t uniformMask) +Shader::Shader(GLRenderManager *render, const char *code, const char *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; + std::string descstr(desc); #ifdef SHADERLOG #ifdef _WIN32 OutputDebugStringUTF8(code); @@ -56,7 +57,7 @@ Shader::Shader(GLRenderManager *render, const char *code, uint32_t glShaderType, printf("%s\n", code); #endif #endif - shader = render->CreateShader(glShaderType, source_); + shader = render->CreateShader(glShaderType, source_, descstr); } Shader::~Shader() { @@ -608,7 +609,8 @@ Shader *ShaderManagerGLES::CompileFragmentShader(FShaderID FSID) { if (!GenerateFragmentShader(FSID, codeBuffer_, &uniformMask)) { return nullptr; } - return new Shader(render_, codeBuffer_, GL_FRAGMENT_SHADER, false, 0, uniformMask); + std::string desc = FragmentShaderDesc(FSID); + return new Shader(render_, codeBuffer_, desc.c_str(), GL_FRAGMENT_SHADER, false, 0, uniformMask); } Shader *ShaderManagerGLES::CompileVertexShader(VShaderID VSID) { @@ -616,7 +618,8 @@ Shader *ShaderManagerGLES::CompileVertexShader(VShaderID VSID) { uint32_t attrMask; uint64_t uniformMask; GenerateVertexShader(VSID, codeBuffer_, &attrMask, &uniformMask); - return new Shader(render_, codeBuffer_, GL_VERTEX_SHADER, useHWTransform, attrMask, uniformMask); + std::string desc = VertexShaderDesc(VSID); + return new Shader(render_, codeBuffer_, desc.c_str(), GL_VERTEX_SHADER, useHWTransform, attrMask, uniformMask); } Shader *ShaderManagerGLES::ApplyVertexShader(int prim, u32 vertType, VShaderID *VSID) { @@ -666,7 +669,7 @@ Shader *ShaderManagerGLES::ApplyVertexShader(int prim, u32 vertType, VShaderID * uint32_t attrMask; uint64_t uniformMask; GenerateVertexShader(vsidTemp, codeBuffer_, &attrMask, &uniformMask); - vs = new Shader(render_, codeBuffer_, GL_VERTEX_SHADER, false, attrMask, uniformMask); + vs = new Shader(render_, codeBuffer_, VertexShaderDesc(vsidTemp).c_str(), GL_VERTEX_SHADER, false, attrMask, uniformMask); } vsCache_.Insert(*VSID, vs); diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index 34ab88a7d8..b7ee34216b 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -125,12 +125,12 @@ public: class Shader { public: - Shader(GLRenderManager *render, const char *code, uint32_t glShaderType, bool useHWTransform, uint32_t attrMask, uint64_t uniformMask); + Shader(GLRenderManager *render, const char *code, const char *desc, uint32_t glShaderType, bool useHWTransform, uint32_t attrMask, uint64_t uniformMask); ~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; diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 1cd17c8c38..a2cd28166f 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -121,8 +121,8 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe vs_code = ApplyGLSLPrelude(stencil_vs, GL_VERTEX_SHADER); fs_code = ApplyGLSLPrelude(stencil_fs, GL_FRAGMENT_SHADER); std::vector shaders; - shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vs_code)); - shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fs_code)); + shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vs_code, "stencil")); + shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fs_code, "stencil")); std::vector queries; queries.push_back({ &u_stencilUploadTex, "tex" }); std::vector inits; diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index fd8286511a..c31bf38f4e 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -1,3 +1,4 @@ +#include "Core/Reporting.h" #include "GLQueueRunner.h" #include "GLRenderManager.h" #include "DataFormatGL.h" @@ -142,7 +143,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { step.create_shader.shader->shader = shader; const char *code = step.create_shader.code; glShaderSource(shader, 1, &code, nullptr); - delete[] code; + delete[] step.create_shader.code; glCompileShader(shader); GLint success = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); @@ -152,11 +153,20 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { GLsizei len = 0; glGetShaderInfoLog(shader, MAX_INFO_LOG_SIZE, &len, infoLog); infoLog[len] = '\0'; - glDeleteShader(shader); - shader = 0; - ILOG("%s Shader compile error:\n%s", step.create_shader.stage == GL_FRAGMENT_SHADER ? "Fragment" : "Vertex", infoLog); +#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 for: %s", step.create_shader.desc); + 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, step.create_shader.desc, (const char *)code); +#ifdef SHADERLOG + OutputDebugStringUTF8(infoLog); +#endif step.create_shader.shader->valid = false; } + delete[] step.create_shader.desc; step.create_shader.shader->valid = true; break; } diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index 6bea398557..f11e9fec44 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -193,7 +193,9 @@ struct GLRInitStep { } create_texture; struct { GLRShader *shader; + // These char arrays need to be allocated with new[]. char *code; + char *desc; // For error logging. GLuint stage; } create_shader; struct { diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 22e5679258..c351ed307a 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -218,12 +218,16 @@ public: return step.create_buffer.buffer; } - GLRShader *CreateShader(GLuint stage, std::string &code) { + GLRShader *CreateShader(GLuint stage, std::string code, std::string desc) { GLRInitStep step{ GLRInitStepType::CREATE_SHADER }; step.create_shader.shader = new GLRShader(); step.create_shader.stage = stage; step.create_shader.code = new char[code.size() + 1]; memcpy(step.create_shader.code, code.data(), code.size() + 1); + if (!desc.empty()) { + step.create_shader.desc = desc.size() ? new char[desc.size() + 1] : nullptr; + memcpy(step.create_shader.desc, desc.data(), desc.size() + 1); + } initSteps_.push_back(step); return step.create_shader.shader; } diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 83ae2c484f..9df92b2c25 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -272,7 +272,7 @@ bool OpenGLShaderModule::Compile(GLRenderManager *render, ShaderLanguage languag source_ = temp.c_str(); } - shader_ = render->CreateShader(glstage_, source_); + shader_ = render->CreateShader(glstage_, source_, "thin3d"); return true; } From a642c1a990056970fa1d8e4262344c43d4808abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 10 Jan 2018 11:02:54 +0100 Subject: [PATCH 39/96] Fix goof with the stencil buffer drawing --- GPU/GLES/StencilBufferGLES.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index a2cd28166f..07eb171d89 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -178,13 +178,13 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe continue; } if (dstBuffer->format == GE_FORMAT_4444) { - render_->SetStencilOp((i << 4) | i, GL_KEEP, GL_KEEP, GL_KEEP); + 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) { - render_->SetStencilOp(0xFF, GL_KEEP, GL_KEEP, GL_KEEP); + render_->SetStencilOp(0xFF, GL_REPLACE, GL_REPLACE, GL_REPLACE); render_->SetUniformF1(&u_stencilValue, i * (128.0f / 255.0f)); } else { - render_->SetStencilOp(i, GL_KEEP, GL_KEEP, GL_KEEP); + 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); From 58854adb80a4d46469fe095f01ac5034e4dd395b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 10 Jan 2018 11:13:51 +0100 Subject: [PATCH 40/96] StencilBufferGLES: Move SetNoBlendAndMask to the right places. --- GPU/GLES/StencilBufferGLES.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 07eb171d89..33bea5daba 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -110,7 +110,9 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe } // Let's not bother with the shader if it's just zero. + render_->SetNoBlendAndMask(0x8); render_->Clear(0, 0, 0, GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + render_->SetNoBlendAndMask(0xF); gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE); return true; } @@ -143,7 +145,6 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe shaderManagerGL_->DirtyLastShader(); DisableState(); - render_->SetNoBlendAndMask(0x8); bool useBlit = gstate_c.Supports(GPU_SUPPORTS_ARB_FRAMEBUFFER_BLIT | GPU_SUPPORTS_NV_FRAMEBUFFER_BLIT); @@ -169,6 +170,7 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1); textureCacheGL_->ForgetLastTexture(); + render_->SetNoBlendAndMask(0x8); render_->Clear(0, 0, 0, GL_STENCIL_BUFFER_BIT); render_->SetStencilFunc(GL_TRUE, GL_ALWAYS, 0xFF, 0xFF); @@ -194,8 +196,8 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe draw_->BlitFramebuffer(blitFBO, 0, 0, w, h, dstBuffer->fbo, 0, 0, dstBuffer->renderWidth, dstBuffer->renderHeight, Draw::FB_STENCIL_BIT, Draw::FB_BLIT_NEAREST); } + render_->SetNoBlendAndMask(0xF); gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE); - RebindFramebuffer(); return true; } From d0038775e5436b5aaa29f3aff78e0945ec51d8e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 10 Jan 2018 11:22:54 +0100 Subject: [PATCH 41/96] TODOs and indentations --- GPU/GLES/DrawEngineGLES.cpp | 1 + GPU/GLES/FramebufferManagerGLES.cpp | 4 ++-- GPU/GLES/StateMappingGLES.cpp | 1 + ext/native/thin3d/GLQueueRunner.cpp | 1 - 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index d6d803ed12..1aa3a0dd66 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -759,6 +759,7 @@ 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) { + // TODO: Implement with the render manager /* #ifndef USING_GLES2 if (isAllowTexture1D_) { diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 67d9ce44cb..60bddd9499 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -293,8 +293,8 @@ void FramebufferManagerGLES::DestroyDeviceObjects() { } if (drawPixelsTex_) { render_->DeleteTexture(drawPixelsTex_); - drawPixelsTex_ = 0; - } + drawPixelsTex_ = 0; + } if (stencilUploadProgram_) { render_->DeleteProgram(stencilUploadProgram_); stencilUploadProgram_ = nullptr; diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 1c8f52911d..0b9229e365 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -232,6 +232,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) { // TODO: Make this dynamic // Logic Ops if (gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY) { + // TODO: Fix logic ops. //glstate.colorLogicOp.enable(); //glstate.logicOp.set(logicOps[gstate.getLogicOp()]); } else { diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index c31bf38f4e..8f63e76f38 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -398,7 +398,6 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { PerformBindFramebufferAsRenderTarget(step); - glDisable(GL_SCISSOR_TEST); glDisable(GL_DEPTH_TEST); glDisable(GL_STENCIL_TEST); glDisable(GL_BLEND); From e4752a887f0f77abea9f4af9c991656dee721ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 10 Jan 2018 16:37:02 +0100 Subject: [PATCH 42/96] Fix use-after-free in shader error reporting --- ext/native/thin3d/GLQueueRunner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 8f63e76f38..4c3260e26e 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -143,7 +143,6 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { step.create_shader.shader->shader = shader; const char *code = step.create_shader.code; glShaderSource(shader, 1, &code, nullptr); - delete[] step.create_shader.code; glCompileShader(shader); GLint success = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); @@ -166,6 +165,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { #endif step.create_shader.shader->valid = false; } + delete[] step.create_shader.code; delete[] step.create_shader.desc; step.create_shader.shader->valid = true; break; From af6431986d9f217a5d506177fecc34f634fa817a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Jan 2018 14:16:56 +0100 Subject: [PATCH 43/96] OpenGL: Now run GL on a secondary thread. Sync issues remain. --- Common/GraphicsContext.h | 5 +-- GPU/GLES/FramebufferManagerGLES.cpp | 1 - UI/EmuScreen.cpp | 2 +- Windows/EmuThread.cpp | 46 +++++++++++++++++++++------ Windows/GPU/WindowsGLContext.cpp | 26 +++++++++++---- Windows/GPU/WindowsGLContext.h | 11 ++++++- Windows/main.cpp | 4 ++- ext/native/thin3d/GLRenderManager.cpp | 27 ++++++++++------ ext/native/thin3d/GLRenderManager.h | 23 ++++++++++++-- 9 files changed, 110 insertions(+), 35 deletions(-) diff --git a/Common/GraphicsContext.h b/Common/GraphicsContext.h index e32dd6c306..64a5141379 100644 --- a/Common/GraphicsContext.h +++ b/Common/GraphicsContext.h @@ -10,10 +10,7 @@ class GraphicsContext { public: virtual ~GraphicsContext() {} - // Threaded backends (that need to do init on the final render thread, like GL) - // call this from the render thread. Init() should block until InitFromThread is done. - // Other backends can ignore this. - virtual bool InitFromThread() { return true; } + virtual bool InitFromRenderThread(std::string *errorMessage) { return true; } virtual void Shutdown() = 0; virtual void SwapInterval(int interval) = 0; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 60bddd9499..21d0653e08 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -213,7 +213,6 @@ void FramebufferManagerGLES::CompilePostShader() { postShaderProgram_ = nullptr; usePostShader_ = false; } - glsl_unbind(); } void FramebufferManagerGLES::Bind2DShader() { diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 439aaf3444..1575109cd0 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -254,7 +254,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) { diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 36dd44c34d..7e5ed4f48f 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -33,6 +33,9 @@ static std::thread renderThread; static std::atomic renderThreadReady; static bool useRenderThread; +static bool renderThreadFailed; +static bool renderThreadSucceeded; +static std::string g_error_message; extern std::vector GetWideCmdLine(); @@ -85,15 +88,23 @@ bool EmuThread_Ready() { void RenderThreadFunc() { setCurrentThreadName("Render"); + renderThreadFailed = false; + renderThreadSucceeded = false; while (!g_graphicsContext) { - sleep_ms(50); + sleep_ms(10); continue; } - g_graphicsContext->InitFromThread(); - while (true) { - g_graphicsContext->ThreadFrame(); - break; + + std::string error_message; + if (!g_graphicsContext->InitFromRenderThread(&error_message)) { + g_error_message = error_message; + renderThreadFailed = true; + return; + } else { + renderThreadSucceeded = true; } + + g_graphicsContext->ThreadFrame(); } void EmuThreadFunc() { @@ -119,10 +130,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. @@ -171,7 +197,7 @@ void EmuThreadFunc() { exit(1); } - NativeInitGraphics(graphicsContext); + NativeInitGraphics(g_graphicsContext); NativeResized(); INFO_LOG(BOOT, "Done."); @@ -194,7 +220,7 @@ void EmuThreadFunc() { // This way they can load a new game. if (!Core_IsActive()) UpdateUIState(UISTATE_MENU); - Core_Run(graphicsContext); + Core_Run(g_graphicsContext); } shutdown: diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index b4c0692246..08bcc9a5b0 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -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,8 @@ #include "Windows/GPU/WindowsGLContext.h" void WindowsGLContext::SwapBuffers() { - ::SwapBuffers(hDC); + renderManager_->Swap(); + // Used during fullscreen switching to prevent rendering. if (pauseRequested) { SetEvent(pauseEvent); @@ -151,10 +153,16 @@ void DebugCallbackARB(GLenum source, GLenum type, GLuint id, GLenum severity, } bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_message) { + hInst_ = hInst; + hWnd_ = window; + *error_message = "ok"; + return true; +} + +bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { glslang::InitializeProcess(); *error_message = "ok"; - hWnd = window; GLuint PixelFormat; // TODO: Change to use WGL_ARB_pixel_format instead @@ -179,7 +187,7 @@ 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."; @@ -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 @@ -393,16 +403,20 @@ void WindowsGLContext::Shutdown() { 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); } hDC = NULL; } - hWnd = NULL; + hWnd_ = NULL; glslang::FinalizeProcess(); } void WindowsGLContext::Resize() { } + +void WindowsGLContext::ThreadFrame() { + renderManager_->ThreadFunc(); +} diff --git a/Windows/GPU/WindowsGLContext.h b/Windows/GPU/WindowsGLContext.h index 4ddd28ba61..b628ee25d2 100644 --- a/Windows/GPU/WindowsGLContext.h +++ b/Windows/GPU/WindowsGLContext.h @@ -7,10 +7,14 @@ 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 Shutdown() override; void SwapInterval(int interval) override; void SwapBuffers() override; @@ -21,13 +25,18 @@ public: void Resume() override; void Resize() override; + void 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; diff --git a/Windows/main.cpp b/Windows/main.cpp index 5bbe6ec01a..66c09110ba 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -530,7 +530,9 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin } // Emu thread (and render thread, if any) is always running! - EmuThread_Start(false); // g_Config.iGPUBackend == GPU_BACKEND_VULKAN); + // 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); diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 452497d3e2..33d1be696f 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -44,7 +44,8 @@ GLRenderManager::GLRenderManager() { } if (!useThread_) { - queueRunner_.CreateDeviceObjects(); + // The main thread is also the render thread. + ThreadStartup(); } } @@ -54,16 +55,22 @@ GLRenderManager::~GLRenderManager() { } if (!useThread_) { - queueRunner_.DestroyDeviceObjects(); + // The main thread is also the render thread. + ThreadEnd(); } } -void GLRenderManager::ThreadFunc() { - setCurrentThreadName("RenderMan"); - int threadFrame = threadInitFrame_; - bool nextFrame = false; - bool firstFrame = true; +void GLRenderManager::ThreadStartup() { queueRunner_.CreateDeviceObjects(); +} + +void GLRenderManager::ThreadEnd() { + queueRunner_.DestroyDeviceObjects(); +} + +void GLRenderManager::ThreadFunc() { + ThreadStartup(); + int threadFrame = threadInitFrame_; while (true) { { if (nextFrame) { @@ -344,7 +351,9 @@ void GLRenderManager::EndSubmitFrame(int frame) { Submit(frame, true); if (!frameData.skipSwap) { - // glSwapBuffers(); + if (swapFunction_) { + swapFunction_(); + } } else { frameData.skipSwap = false; } @@ -540,4 +549,4 @@ size_t GLPushBuffer::GetTotalSize() const { sum += size_ * (buffers_.size() - 1); sum += offset_; return sum; -} \ No newline at end of file +} diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index c351ed307a..d6716f626b 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -622,7 +623,20 @@ public: frameData_[frame].activePushBuffers.erase(iter); } + void SetSwapFunction(std::function swapFunction) { + swapFunction_ = swapFunction; + } + + void Swap() { + if (!useThread_ && swapFunction_) { + swapFunction_(); + } + } + private: + void ThreadStartup(); + void ThreadEnd(); + void BeginSubmitFrame(int frame); void EndSubmitFrame(int frame); void Submit(int frame, bool triggerFence); @@ -668,17 +682,22 @@ private: // Execution time state bool run_ = true; - // Thread is managed elsewhere, and should call ThreadFunc. + // Thread is managed elsewhere, and should call ThreadFrame. std::mutex mutex_; int threadInitFrame_ = 0; GLQueueRunner queueRunner_; + bool nextFrame = false; + bool firstFrame = true; + GLDeleter deleter_; - bool useThread_ = false; + bool useThread_ = true; int curFrame_ = 0; + std::function swapFunction_; + int targetWidth_ = 0; int targetHeight_ = 0; }; From e5d571c25a3ef4854801d5a180267200f61c42d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Jan 2018 15:19:35 +0100 Subject: [PATCH 44/96] gl-render-manager: Now threaded mode seems to work fine (and fast!). Shutdown doesn't, though, and there are stability issues. --- Windows/GPU/WindowsGLContext.cpp | 2 +- ext/native/thin3d/GLQueueRunner.cpp | 4 +++- ext/native/thin3d/GLRenderManager.cpp | 6 +++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index 08bcc9a5b0..f4285d017b 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -116,7 +116,7 @@ void FormatDebugOutputARB(char outStr[], size_t outStrSize, GLenum source, GLenu } snprintf(severityStr, sizeof(severityStr), severityFmt, severity); - snprintf(outStr, sizeof(outStr), "OpenGL: %s [source=%s type=%s severity=%s id=%d]\n", msg, sourceStr, typeStr, severityStr, id); + 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, diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 4c3260e26e..ee4dfb2737 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -735,7 +735,9 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { tex->minFilter = c.textureSampler.minFilter; } if (tex->anisotropy != c.textureSampler.anisotropy) { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, c.textureSampler.anisotropy); + if (c.textureSampler.anisotropy != 0.0f) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, c.textureSampler.anisotropy); + } tex->anisotropy = c.textureSampler.anisotropy; } break; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 33d1be696f..b4c9535601 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -285,7 +285,7 @@ void GLRenderManager::BeginFrame() { } // vulkan_->BeginFrame(); - frameData.deleter.Perform(); + // In GL, we have to do deletes on the submission thread. insideFrame_ = true; } @@ -328,6 +328,10 @@ void GLRenderManager::BeginSubmitFrame(int frame) { if (!frameData.hasBegun) { frameData.hasBegun = true; } + + // This should be the best time to perform deletes for the next frame. + FrameData &nextFrameData = frameData_[(frame + 1) % MAX_INFLIGHT_FRAMES]; + nextFrameData.deleter.Perform(); } void GLRenderManager::Submit(int frame, bool triggerFence) { From 57615344e4e0d7c696de9a531fc504a9dba04131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Jan 2018 17:32:50 +0100 Subject: [PATCH 45/96] Fix for deletes, add some debugging stuff --- Common/Vulkan/VulkanContext.cpp | 32 +++++++++++----------- ext/native/thin3d/GLQueueRunner.cpp | 2 ++ ext/native/thin3d/GLRenderManager.cpp | 39 ++++++++++++++------------- ext/native/thin3d/GLRenderManager.h | 12 +++++++++ 4 files changed, 51 insertions(+), 34 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index 6f3cfb5d55..8854970e9d 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -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_); diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index ee4dfb2737..4b6bedada7 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -187,6 +187,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { case GLRInitStepType::TEXTURE_IMAGE: { GLRTexture *tex = step.texture_image.texture; + _assert_msg_(G3D, tex->canary == GLRTexture::CanaryValue, "Canary has bad value %08x in texture %p", tex->canary, tex); CHECK_GL_ERROR_IF_DEBUG(); if (boundTexture != tex->texture) { glBindTexture(tex->target, tex->texture); @@ -624,6 +625,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } if (c.texture.texture) { if (curTex[slot] != c.texture.texture) { + _assert_msg_(G3D, c.texture.texture->canary == GLRTexture::CanaryValue, "Canary has bad value %08x in texture %p", c.texture.texture->canary, c.texture.texture); glBindTexture(c.texture.texture->target, c.texture.texture->texture); curTex[slot] = c.texture.texture; } diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index b4c9535601..d4532489c9 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -12,6 +12,7 @@ #endif void GLDeleter::Perform() { + deleterMutex_.lock(); for (auto shader : shaders) { delete shader; } @@ -36,6 +37,7 @@ void GLDeleter::Perform() { delete framebuffer; } framebuffers.clear(); + deleterMutex_.unlock(); } GLRenderManager::GLRenderManager() { @@ -62,6 +64,7 @@ GLRenderManager::~GLRenderManager() { void GLRenderManager::ThreadStartup() { queueRunner_.CreateDeviceObjects(); + threadFrame_ = threadInitFrame_; } void GLRenderManager::ThreadEnd() { @@ -70,25 +73,24 @@ void GLRenderManager::ThreadEnd() { void GLRenderManager::ThreadFunc() { ThreadStartup(); - int threadFrame = threadInitFrame_; while (true) { { if (nextFrame) { - threadFrame++; - if (threadFrame >= MAX_INFLIGHT_FRAMES) - threadFrame = 0; + threadFrame_++; + if (threadFrame_ >= MAX_INFLIGHT_FRAMES) + threadFrame_ = 0; } - FrameData &frameData = frameData_[threadFrame]; + FrameData &frameData = frameData_[threadFrame_]; std::unique_lock lock(frameData.pull_mutex); while (!frameData.readyForRun && run_) { - VLOG("PULL: Waiting for frame[%d].readyForRun", threadFrame); + 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. break; } - VLOG("PULL: frame[%d].readyForRun = false", threadFrame); + 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. @@ -97,15 +99,15 @@ void GLRenderManager::ThreadFunc() { nextFrame = frameData.type == GLRRunType::END; assert(frameData.type == GLRRunType::END || frameData.type == GLRRunType::SYNC); } - VLOG("PULL: Running frame %d", threadFrame); + VLOG("PULL: Running frame %d", threadFrame_); if (firstFrame) { - ILOG("Running first frame (%d)", threadFrame); + ILOG("Running first frame (%d)", threadFrame_); firstFrame = false; } - Run(threadFrame); - VLOG("PULL: Finished frame %d", threadFrame); + Run(threadFrame_); + VLOG("PULL: Finished frame %d", threadFrame_); } - queueRunner_.DestroyDeviceObjects(); + ThreadEnd(); VLOG("PULL: Quitting"); } @@ -113,7 +115,6 @@ void GLRenderManager::ThreadFunc() { 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++) { @@ -274,6 +275,7 @@ void GLRenderManager::BeginFrame() { VLOG("PUSH: Fencing %d", curFrame); + // vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); // vkResetFences(device, 1, &frameData.fence); // glFenceSync(...) @@ -313,7 +315,6 @@ void GLRenderManager::Finish() { frameData.pull_condVar.notify_all(); } - // vulkan_->EndFrame(); frameData_[curFrame_].deleter.Take(deleter_); curFrame_++; @@ -328,10 +329,6 @@ void GLRenderManager::BeginSubmitFrame(int frame) { if (!frameData.hasBegun) { frameData.hasBegun = true; } - - // This should be the best time to perform deletes for the next frame. - FrameData &nextFrameData = frameData_[(frame + 1) % MAX_INFLIGHT_FRAMES]; - nextFrameData.deleter.Perform(); } void GLRenderManager::Submit(int frame, bool triggerFence) { @@ -340,8 +337,14 @@ void GLRenderManager::Submit(int frame, bool triggerFence) { // In GL, submission happens automatically in Run(). // When !triggerFence, we notify after syncing with Vulkan. + + // Putting deletes here is safe but only because OpenGL has its own delete handling.. + // ideally we'd like to wait a frame or two. + frameData.deleter.Perform(); + if (useThread_ && triggerFence) { VLOG("PULL: Frame %d.readyForFence = true", frame); + std::unique_lock lock(frameData.push_mutex); frameData.readyForFence = true; frameData.push_condVar.notify_all(); diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index d6716f626b..dd8b3b29c5 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -111,7 +111,12 @@ public: if (texture) { glDeleteTextures(1, &texture); } + canary = 0xd31373d; // deleted } + enum { + CanaryValue = 0x12345678, + }; + uint32_t canary = CanaryValue; GLuint texture = 0; // Could also trust OpenGL defaults I guess.. GLenum target = 0xFFFF; @@ -149,6 +154,8 @@ public: void Perform(); void Take(GLDeleter &other) { + deleterMutex_.lock(); + _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); @@ -161,6 +168,7 @@ public: other.textures.clear(); other.inputLayouts.clear(); other.framebuffers.clear(); + deleterMutex_.unlock(); } std::vector shaders; @@ -169,6 +177,7 @@ public: std::vector textures; std::vector inputLayouts; std::vector framebuffers; + std::mutex deleterMutex_; }; class GLRInputLayout { @@ -687,6 +696,9 @@ private: int threadInitFrame_ = 0; GLQueueRunner queueRunner_; + // Thread state + int threadFrame_; + bool nextFrame = false; bool firstFrame = true; From fdca06d20813c8eeb15e19d03a90f104ac061a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Jan 2018 18:13:31 +0100 Subject: [PATCH 46/96] More work on shutdown, still hanging though. --- Common/GraphicsContext.h | 5 +- Windows/EmuThread.cpp | 10 +++- Windows/GPU/WindowsGLContext.cpp | 21 ++++++-- Windows/GPU/WindowsGLContext.h | 5 +- android/jni/app-android.cpp | 1 - ext/native/thin3d/GLRenderManager.cpp | 74 +++++++++++++-------------- ext/native/thin3d/GLRenderManager.h | 11 ++-- 7 files changed, 73 insertions(+), 54 deletions(-) diff --git a/Common/GraphicsContext.h b/Common/GraphicsContext.h index 64a5141379..66afcd94da 100644 --- a/Common/GraphicsContext.h +++ b/Common/GraphicsContext.h @@ -11,6 +11,7 @@ public: virtual ~GraphicsContext() {} virtual bool InitFromRenderThread(std::string *errorMessage) { return true; } + virtual void ShutdownFromRenderThread() {} virtual void Shutdown() = 0; virtual void SwapInterval(int interval) = 0; @@ -28,7 +29,9 @@ public: virtual void *GetAPIContext() { return nullptr; } // Called from the render thread from threaded backends. - virtual void ThreadFrame() {} + virtual void ThreadStart() {} + virtual bool ThreadFrame() { return true; } + virtual void ThreadEnd() {} virtual Draw::DrawContext *GetDrawContext() = 0; }; diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 7e5ed4f48f..da187068d6 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -104,7 +104,13 @@ void RenderThreadFunc() { renderThreadSucceeded = true; } - g_graphicsContext->ThreadFrame(); + g_graphicsContext->ThreadStart(); + while (emuThreadState != THREAD_SHUTDOWN) { + if (!g_graphicsContext->ThreadFrame()) + break; + } + g_graphicsContext->ThreadEnd(); + g_graphicsContext->ShutdownFromRenderThread(); } void EmuThreadFunc() { @@ -227,6 +233,8 @@ shutdown: emuThreadState = THREAD_SHUTDOWN; NativeShutdownGraphics(); + if (!useRenderThread) + g_graphicsContext->ShutdownFromRenderThread(); // NativeShutdown deletes the graphics context through host->ShutdownGraphics(). NativeShutdown(); diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index f4285d017b..ac48df35fd 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -153,6 +153,8 @@ 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"; @@ -160,8 +162,6 @@ bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_mes } bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { - glslang::InitializeProcess(); - *error_message = "ok"; GLuint PixelFormat; @@ -386,6 +386,10 @@ void WindowsGLContext::SwapInterval(int interval) { } void WindowsGLContext::Shutdown() { + glslang::FinalizeProcess(); +} + +void WindowsGLContext::ShutdownFromRenderThread() { delete draw_; draw_ = nullptr; CloseHandle(pauseEvent); @@ -411,12 +415,19 @@ void WindowsGLContext::Shutdown() { hDC = NULL; } hWnd_ = NULL; - glslang::FinalizeProcess(); } void WindowsGLContext::Resize() { } -void WindowsGLContext::ThreadFrame() { - renderManager_->ThreadFunc(); +void WindowsGLContext::ThreadStart() { + renderManager_->ThreadStart(); +} + +bool WindowsGLContext::ThreadFrame() { + return renderManager_->ThreadFrame(); +} + +void WindowsGLContext::ThreadEnd() { + renderManager_->ThreadEnd(); } diff --git a/Windows/GPU/WindowsGLContext.h b/Windows/GPU/WindowsGLContext.h index b628ee25d2..38c9494947 100644 --- a/Windows/GPU/WindowsGLContext.h +++ b/Windows/GPU/WindowsGLContext.h @@ -14,6 +14,7 @@ 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; @@ -25,7 +26,9 @@ public: void Resume() override; void Resize() override; - void ThreadFrame() override; + void ThreadStart() override; + void ThreadEnd() override; + bool ThreadFrame() override; Draw::DrawContext *GetDrawContext() override { return draw_; } diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 48fe18013c..97ce69edc4 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -470,7 +470,6 @@ 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; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index d4532489c9..85cd8b5089 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -47,7 +47,7 @@ GLRenderManager::GLRenderManager() { if (!useThread_) { // The main thread is also the render thread. - ThreadStartup(); + ThreadStart(); } } @@ -62,54 +62,50 @@ GLRenderManager::~GLRenderManager() { } } -void GLRenderManager::ThreadStartup() { +void GLRenderManager::ThreadStart() { queueRunner_.CreateDeviceObjects(); threadFrame_ = threadInitFrame_; } void GLRenderManager::ThreadEnd() { queueRunner_.DestroyDeviceObjects(); + VLOG("PULL: Quitting"); } -void GLRenderManager::ThreadFunc() { - ThreadStartup(); - while (true) { - { - if (nextFrame) { - threadFrame_++; - if (threadFrame_ >= MAX_INFLIGHT_FRAMES) - threadFrame_ = 0; - } - FrameData &frameData = frameData_[threadFrame_]; - std::unique_lock 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. - break; - } - 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. +bool GLRenderManager::ThreadFrame() { + { + if (nextFrame) { + threadFrame_++; + if (threadFrame_ >= MAX_INFLIGHT_FRAMES) + threadFrame_ = 0; + } + FrameData &frameData = frameData_[threadFrame_]; + std::unique_lock 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_); + // Only increment next time if we're done. + nextFrame = frameData.type == GLRRunType::END; + assert(frameData.type == GLRRunType::END || frameData.type == GLRRunType::SYNC); } - ThreadEnd(); - - VLOG("PULL: Quitting"); + VLOG("PULL: Running frame %d", threadFrame_); + if (firstFrame) { + ILOG("Running first frame (%d)", threadFrame_); + firstFrame = false; + } + Run(threadFrame_); + VLOG("PULL: Finished frame %d", threadFrame_); + return true; } void GLRenderManager::StopThread() { diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index dd8b3b29c5..0778f35339 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -199,7 +199,9 @@ public: GLRenderManager(); ~GLRenderManager(); - void ThreadFunc(); + 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(); @@ -642,10 +644,9 @@ public: } } -private: - void ThreadStartup(); - void ThreadEnd(); + void StopThread(); +private: void BeginSubmitFrame(int frame); void EndSubmitFrame(int frame); void Submit(int frame, bool triggerFence); @@ -654,8 +655,6 @@ private: void FlushSync(); void EndSyncFrame(int frame); - void StopThread(); - // Per-frame data, round-robin so we can overlap submission with execution of the previous frame. struct FrameData { std::mutex push_mutex; From 4f1b8d80a9c55071d12a83876f34fed2cf861afb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 18 Jan 2018 21:13:30 -0800 Subject: [PATCH 47/96] GLES: Bind texture on MakePixelTexture(). This is currently necessary because callers expect it. Also, move inside render passes. --- GPU/Common/FramebufferCommon.cpp | 4 ++-- GPU/GLES/FramebufferManagerGLES.cpp | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index c0d747802f..718500058b 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -719,8 +719,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); @@ -736,6 +734,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); diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 21d0653e08..711c97b398 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -372,6 +372,9 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma } render_->TextureImage(drawPixelsTex_, 0, texWidth, height, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, convBuf, false); render_->FinalizeTexture(drawPixelsTex_, 0, false); + + // TODO: Return instead? + render_->BindTexture(0, drawPixelsTex_); } void FramebufferManagerGLES::SetViewport2D(int x, int y, int w, int h) { From a135035850d0e6fc4d9378196ae8a026167ee99b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 18 Jan 2018 21:14:17 -0800 Subject: [PATCH 48/96] GLES: Ensure stencil upload clear in render pass. Fixes crashes in Star Ocean. --- GPU/GLES/StencilBufferGLES.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 33bea5daba..05965b9c59 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -110,6 +110,9 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe } // Let's not bother with the shader if it's just zero. + if (dstBuffer->fbo) { + draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }); + } render_->SetNoBlendAndMask(0x8); render_->Clear(0, 0, 0, GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); render_->SetNoBlendAndMask(0xF); From 790e3121d2d0ff12fde4788561ae372c059ddc3e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 18 Jan 2018 21:15:21 -0800 Subject: [PATCH 49/96] GLES: Move stencil upload state into render pass. Without this, stencil upload simply failed silently. Fixes Star Ocean stencil issues. --- GPU/GLES/StencilBufferGLES.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 05965b9c59..4345f1b14f 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -130,6 +130,7 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fs_code, "stencil")); std::vector queries; queries.push_back({ &u_stencilUploadTex, "tex" }); + queries.push_back({ &u_stencilValue, "u_stencilValue" }); std::vector inits; inits.push_back({ &u_stencilUploadTex, 0, 0 }); stencilUploadProgram_ = render_->CreateProgram(shaders, {}, queries, inits, false); @@ -138,11 +139,7 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe } if (!stencilUploadProgram_) { ERROR_LOG_REPORT(G3D, "Failed to compile stencilUploadProgram! This shouldn't happen.\n%s", errorString.c_str()); - } else { - render_->BindProgram(stencilUploadProgram_); } - } else { - render_->BindProgram(stencilUploadProgram_); } shaderManagerGL_->DirtyLastShader(); @@ -173,9 +170,11 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1); textureCacheGL_->ForgetLastTexture(); - render_->SetNoBlendAndMask(0x8); + // We must bind the program after starting the render pass, and set the color mask after clearing. render_->Clear(0, 0, 0, GL_STENCIL_BUFFER_BIT); render_->SetStencilFunc(GL_TRUE, GL_ALWAYS, 0xFF, 0xFF); + render_->BindProgram(stencilUploadProgram_); + render_->SetNoBlendAndMask(0x8); for (int i = 1; i < values; i += i) { if (!(usedBits & i)) { From 22bf124631e85fccfcc01989b9523981116ef052 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 18 Jan 2018 21:16:06 -0800 Subject: [PATCH 50/96] GLES: Document stencil clearing bug. --- GPU/GLES/StencilBufferGLES.cpp | 1 + ext/native/thin3d/GLQueueRunner.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 4345f1b14f..980c3d4406 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -113,6 +113,7 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe if (dstBuffer->fbo) { draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }); } + // TODO: This incorrectly does not apply to the clear currently. render_->SetNoBlendAndMask(0x8); render_->Clear(0, 0, 0, GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); render_->SetNoBlendAndMask(0xF); diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 4b6bedada7..0434a997f8 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -498,6 +498,8 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { break; case GLRRenderCommand::CLEAR: glDisable(GL_SCISSOR_TEST); + // TODO: We sometimes pass a color mask in that we want to respect (StencilBufferGLES.) + // We want to clear to only clear alpha, in that case. glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); colorMask = 0xF; if (c.clear.clearMask & GL_COLOR_BUFFER_BIT) { From 3eb6d38d75332d7dd23a9602aa06dffca936ccf7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 18 Jan 2018 21:34:52 -0800 Subject: [PATCH 51/96] Vulkan: Fix stencil-only clear for stencil upload. Fixes stencil issues in Star Ocean. --- GPU/Vulkan/StencilBufferVulkan.cpp | 15 ++++++++------- ext/native/thin3d/GLRenderManager.cpp | 9 ++++++--- ext/native/thin3d/GLRenderManager.h | 2 +- ext/native/thin3d/VulkanQueueRunner.cpp | 6 +++--- ext/native/thin3d/VulkanQueueRunner.h | 3 ++- ext/native/thin3d/VulkanRenderManager.cpp | 12 +++++++----- ext/native/thin3d/VulkanRenderManager.h | 2 +- ext/native/thin3d/thin3d_gl.cpp | 3 ++- ext/native/thin3d/thin3d_vulkan.cpp | 3 ++- 9 files changed, 32 insertions(+), 23 deletions(-) diff --git a/GPU/Vulkan/StencilBufferVulkan.cpp b/GPU/Vulkan/StencilBufferVulkan.cpp index 8ee4666583..cb873765f1 100644 --- a/GPU/Vulkan/StencilBufferVulkan.cpp +++ b/GPU/Vulkan/StencilBufferVulkan.cpp @@ -155,21 +155,22 @@ bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skip // 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); diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 85cd8b5089..3cbc3a2dd8 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -157,11 +157,11 @@ void GLRenderManager::StopThread() { } } -void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil) { +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) { + 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; } @@ -184,8 +184,11 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende data.clear.clearColor = clearColor; } if (depth == GLRRenderPassAction::CLEAR) { - clearMask |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; + clearMask |= GL_DEPTH_BUFFER_BIT; data.clear.clearZ = clearDepth; + } + if (stencil == GLRRenderPassAction::CLEAR) { + clearMask |= GL_STENCIL_BUFFER_BIT; data.clear.clearStencil = clearStencil; } if (clearMask) { diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 0778f35339..711aa143fb 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -310,7 +310,7 @@ public: deleter_.framebuffers.push_back(framebuffer); } - void BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); + 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(GLuint texture, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride); diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index e693dc834a..5cd4008386 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -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; diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 1049a5bb80..b52b150fa3 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -108,7 +108,8 @@ struct VKRStep { struct { VKRFramebuffer *framebuffer; VKRRenderPassAction color; - VKRRenderPassAction depthStencil; + VKRRenderPassAction depth; + VKRRenderPassAction stencil; uint32_t clearColor; float clearDepth; int clearStencil; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index ab9bbafc22..ef262e9dc1 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -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; diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index b074e3b4f9..d97ebf42ca 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -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); diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 9df92b2c25..82d66dd0e1 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -1090,8 +1090,9 @@ void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const Render OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; GLRRenderPassAction color = (GLRRenderPassAction)rp.color; GLRRenderPassAction depth = (GLRRenderPassAction)rp.depth; + GLRRenderPassAction stencil = (GLRRenderPassAction)rp.stencil; - renderManager_.BindFramebufferAsRenderTarget(fb ? fb->framebuffer : nullptr, color, depth, rp.clearColor, rp.clearDepth, rp.clearStencil); + renderManager_.BindFramebufferAsRenderTarget(fb ? fb->framebuffer : nullptr, color, depth, stencil, rp.clearColor, rp.clearDepth, rp.clearStencil); } void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int srcX, int srcY, int srcZ, Framebuffer *fbdst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) { diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 2dac9803fa..fd223172ca 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1339,8 +1339,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; } From 5798c0cc736ff8c4dbdc9ee385513d5bc541174f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 18 Jan 2018 21:38:22 -0800 Subject: [PATCH 52/96] Vulkan: Correct zero stencil upload fast path. --- GPU/Vulkan/StencilBufferVulkan.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/GPU/Vulkan/StencilBufferVulkan.cpp b/GPU/Vulkan/StencilBufferVulkan.cpp index cb873765f1..9a7ce2a728 100644 --- a/GPU/Vulkan/StencilBufferVulkan.cpp +++ b/GPU/Vulkan/StencilBufferVulkan.cpp @@ -116,18 +116,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,6 +138,18 @@ bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skip VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(overrideImageView_, nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); + if (usedBits == 0) { + // 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 + + // Skip the loop. + values = 0; + } + for (int i = 1; i < values; i += i) { if (!(usedBits & i)) { // It's already zero, let's skip it. From e98f265d68943b833b87eebbb5f7ec59876d17c0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 18 Jan 2018 21:45:26 -0800 Subject: [PATCH 53/96] GLES: Prevent writing to gutter in readback. This also read outside the conv buffer. Prevents crash on startup for Brave Story. Now, it just has major graphical issues. --- ext/native/thin3d/GLQueueRunner.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 0434a997f8..92320161ce 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -949,9 +949,10 @@ void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) { void GLQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) { // TODO: Maybe move data format conversion here, and always read back 8888. Drivers // don't usually provide very optimized conversion implementations, though some do. + // Just need to be careful about dithering, which may break Danganronpa. int bpp = (int)Draw::DataFormatSizeInBytes(destFormat); for (int y = 0; y < height; y++) { - memcpy(pixels + y * pixelStride * bpp, readbackBuffer_ + y * width * bpp, pixelStride * bpp); + memcpy(pixels + y * pixelStride * bpp, readbackBuffer_ + y * width * bpp, width * bpp); } } From acb692677babb56105e402ebef836c69b0c3079e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 18 Jan 2018 22:17:29 -0800 Subject: [PATCH 54/96] GLES: Accept a color mask for clears. Otherwise, we end up clearing RGB when we try to only clear alpha, which is fairly common. Makes Valkyria Chronicles 3 action mode visible again. --- GPU/GLES/DrawEngineGLES.cpp | 4 +++- GPU/GLES/StencilBufferGLES.cpp | 7 ++----- ext/native/thin3d/GLQueueRunner.cpp | 8 ++++---- ext/native/thin3d/GLQueueRunner.h | 1 + ext/native/thin3d/GLRenderManager.cpp | 1 + ext/native/thin3d/GLRenderManager.h | 3 ++- ext/native/thin3d/thin3d_gl.cpp | 2 +- 7 files changed, 14 insertions(+), 12 deletions(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 1aa3a0dd66..d5cf5ae824 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -709,11 +709,13 @@ rotateVBO: } 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; - render_->Clear(clearColor, clearDepth, clearColor >> 24, target); + render_->Clear(clearColor, clearDepth, clearColor >> 24, target, rgbaMask); framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason); int scissorX1 = gstate.getScissorX1(); diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 980c3d4406..0f3a19b881 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -113,10 +113,7 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe if (dstBuffer->fbo) { draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }); } - // TODO: This incorrectly does not apply to the clear currently. - render_->SetNoBlendAndMask(0x8); - render_->Clear(0, 0, 0, GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); - render_->SetNoBlendAndMask(0xF); + 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; } @@ -172,7 +169,7 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe textureCacheGL_->ForgetLastTexture(); // We must bind the program after starting the render pass, and set the color mask after clearing. - render_->Clear(0, 0, 0, GL_STENCIL_BUFFER_BIT); + 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); diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 92320161ce..1fcd63fdac 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -498,10 +498,10 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { break; case GLRRenderCommand::CLEAR: glDisable(GL_SCISSOR_TEST); - // TODO: We sometimes pass a color mask in that we want to respect (StencilBufferGLES.) - // We want to clear to only clear alpha, in that case. - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - colorMask = 0xF; + if (c.clear.colorMask != colorMask) { + glColorMask(c.clear.colorMask & 1, (c.clear.colorMask >> 1) & 1, (c.clear.colorMask >> 2) & 1, (c.clear.colorMask >> 3) & 1); + colorMask = c.clear.colorMask; + } if (c.clear.clearMask & GL_COLOR_BUFFER_BIT) { float color[4]; Uint8x4ToFloat4(color, c.clear.clearColor); diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index f11e9fec44..22dfbaef84 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -117,6 +117,7 @@ struct GLRRenderData { float clearZ; int clearStencil; int clearMask; // VK_IMAGE_ASPECT_COLOR_BIT etc + int colorMask; // Like blend, but for the clear. } clear; struct { int slot; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 3cbc3a2dd8..7989f0b662 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -193,6 +193,7 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende } if (clearMask) { data.clear.clearMask = clearMask; + data.clear.colorMask = 0xF; step->commands.push_back(data); } diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 711aa143fb..c5d728505e 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -571,13 +571,14 @@ public: curRenderStep_->commands.push_back(data); } - void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { + 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); } diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 82d66dd0e1..a16411fbe7 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -186,7 +186,7 @@ public: bool depthWriteEnabled; GLuint depthComp; // TODO: Two-sided - GLboolean stencilEnabled; + bool stencilEnabled; GLuint stencilFail; GLuint stencilZFail; GLuint stencilPass; From e531a7da437b6ee681fc54d24f692bc166e73d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 19 Jan 2018 20:24:09 +0100 Subject: [PATCH 55/96] gl-render-manager: Implement logic ops --- GPU/GLES/StateMappingGLES.cpp | 10 +--------- ext/native/thin3d/GLQueueRunner.cpp | 24 ++++++++++++++++++++++++ ext/native/thin3d/GLQueueRunner.h | 6 ++++++ ext/native/thin3d/GLRenderManager.h | 10 ++++++++++ 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 0b9229e365..0b96bb05e4 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -229,15 +229,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) { #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) { - // TODO: Fix logic ops. - //glstate.colorLogicOp.enable(); - //glstate.logicOp.set(logicOps[gstate.getLogicOp()]); - } else { - //glstate.colorLogicOp.disable(); - } + renderManager->SetLogicOp(gstate.isLogicOpEnabled(), logicOps[gstate.getLogicOp()]); } #endif } diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 1fcd63fdac..bec22244ae 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -405,6 +405,9 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glDisable(GL_CULL_FACE); glDisable(GL_DITHER); glEnable(GL_SCISSOR_TEST); +#ifndef USING_GLES2 + glDisable(GL_COLOR_LOGIC_OP); +#endif /* #ifndef USING_GLES2 @@ -430,12 +433,14 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { int colorMask = -1; int depthMask = -1; int depthFunc = -1; + int logicOp = -1; GLuint curArrayBuffer = (GLuint)-1; GLuint curElemArrayBuffer = (GLuint)-1; bool depthEnabled = false; bool blendEnabled = false; bool cullEnabled = false; bool ditherEnabled = false; + bool logicEnabled = false; GLuint blendEqColor = (GLuint)-1; GLuint blendEqAlpha = (GLuint)-1; @@ -496,6 +501,22 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { colorMask = c.blend.mask; } break; + case GLRRenderCommand::LOGICOP: +#ifndef USING_GLES2 + if (c.logic.enabled) { + if (!logicEnabled) { + glEnable(GL_COLOR_LOGIC_OP); + logicEnabled = true; + } + if (logicOp != c.logic.logicOp) { + glLogicOp(c.logic.logicOp); + } + } else if (!c.logic.enabled && logicEnabled) { + glDisable(GL_COLOR_LOGIC_OP); + logicEnabled = false; + } +#endif + break; case GLRRenderCommand::CLEAR: glDisable(GL_SCISSOR_TEST); if (c.clear.colorMask != colorMask) { @@ -814,6 +835,9 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glDisable(GL_STENCIL_TEST); glDisable(GL_BLEND); glDisable(GL_CULL_FACE); +#ifndef USING_GLES2 + glDisable(GL_COLOR_LOGIC_OP); +#endif glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index 22dfbaef84..a4175405f2 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -32,6 +32,7 @@ enum class GLRRenderCommand : uint8_t { STENCILOP, BLEND, BLENDCOLOR, + LOGICOP, UNIFORM4I, UNIFORM4F, UNIFORMMATRIX, @@ -55,6 +56,7 @@ enum class GLRRenderCommand : uint8_t { // 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 { @@ -71,6 +73,10 @@ struct GLRRenderData { struct { float color[4]; } blendColor; + struct { + GLboolean enabled; + GLenum logicOp; + } logic; struct { GLboolean enabled; GLboolean write; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index c5d728505e..345b8ad6f2 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -505,6 +505,16 @@ public: 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 }; From b3ed3ea0fbb1d6dd74418ecdd31df311bb6be42f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 19 Jan 2018 21:19:52 -0800 Subject: [PATCH 56/96] GLES: Fix intra-buffer block transfers. Makes Grand Knights History look a bit better. --- ext/native/thin3d/GLQueueRunner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index bec22244ae..381934224b 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -850,7 +850,7 @@ void GLQueueRunner::PerformCopy(const GLRStep &step) { const GLOffset2D &dstPos = step.copy.dstPos; GLRFramebuffer *src = step.copy.src; - GLRFramebuffer *dst = step.copy.src; + GLRFramebuffer *dst = step.copy.dst; int srcLevel = 0; int dstLevel = 0; From b956263a968764d0d3fb4dd2f05c4e2e02b25d56 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 19 Jan 2018 21:25:56 -0800 Subject: [PATCH 57/96] GLES: Consistently reset state on new steps. It'd be nice to avoid this dependency, but since every step forgets the current state, and these functions are called in a lot of places, it's tricky to manage. Fixes graphics in Grand Knights History (framebuf copy creating a new step, resetting blending state.) --- ext/native/thin3d/GLRenderManager.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 7989f0b662..2cb820982d 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -4,6 +4,7 @@ #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 @@ -198,6 +199,9 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende } 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) { @@ -210,17 +214,20 @@ void GLRenderManager::BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, } void GLRenderManager::CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLOffset2D dstPos, int aspectMask) { - GLRStep * step = new GLRStep{ GLRStepType::COPY }; + 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 }; + GLRStep *step = new GLRStep{ GLRStepType::BLIT }; step->blit.srcRect = srcRect; step->blit.dstRect = dstRect; step->blit.src = src; @@ -228,6 +235,9 @@ void GLRenderManager::BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLR 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) { @@ -238,6 +248,9 @@ bool GLRenderManager::CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspec 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(); From 7316261eac2b3cad6c9d2ae5e355371265b23420 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 19 Jan 2018 21:32:45 -0800 Subject: [PATCH 58/96] Vulkan: Fix alpha clear on stencil upload. We need to do it always, because otherwise alpha is not cleared. --- GPU/Vulkan/StencilBufferVulkan.cpp | 31 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/GPU/Vulkan/StencilBufferVulkan.cpp b/GPU/Vulkan/StencilBufferVulkan.cpp index 9a7ce2a728..febe32c61c 100644 --- a/GPU/Vulkan/StencilBufferVulkan.cpp +++ b/GPU/Vulkan/StencilBufferVulkan.cpp @@ -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; + } } )"; @@ -138,17 +142,12 @@ bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skip VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(overrideImageView_, nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); - if (usedBits == 0) { - // 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 - - // Skip the loop. - values = 0; - } + // 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)) { From bdf7fdc7a34e59b5a22f3b6b5290851e07b56cf3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 19 Jan 2018 21:48:38 -0800 Subject: [PATCH 59/96] GLES: Actually stop the thread on shutdown. Fixes shutdown. --- Windows/GPU/WindowsGLContext.cpp | 2 ++ ext/native/thin3d/GLRenderManager.cpp | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index ac48df35fd..53cf6580f8 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -386,6 +386,8 @@ void WindowsGLContext::SwapInterval(int interval) { } void WindowsGLContext::Shutdown() { + if (renderManager_) + renderManager_->StopThread(); glslang::FinalizeProcess(); } diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 2cb820982d..63c9ecbb8f 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -69,11 +69,16 @@ void GLRenderManager::ThreadStart() { } void GLRenderManager::ThreadEnd() { + // Wait for any shutdown to complete in StopThread(). + std::unique_lock lock(mutex_); queueRunner_.DestroyDeviceObjects(); VLOG("PULL: Quitting"); } bool GLRenderManager::ThreadFrame() { + std::unique_lock lock(mutex_); + if (!run_) + return false; { if (nextFrame) { threadFrame_++; @@ -126,7 +131,8 @@ void GLRenderManager::StopThread() { } } - // TODO: Wait for something here! + // Wait until we've definitely stopped the threadframe. + std::unique_lock lock(mutex_); ILOG("GL submission thread paused. Frame=%d", curFrame_); From db0989a9d0e2d0d298a4e8a8945d4d26b878c650 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 19 Jan 2018 22:41:18 -0800 Subject: [PATCH 60/96] Debugger: Fix texture preview in GLES. Also, fix issues with the first view of a texture in Vulkan / D3D11. --- GPU/D3D11/TextureCacheD3D11.cpp | 14 +++++++------ GPU/GLES/TextureCacheGLES.cpp | 29 +++++++++++++++++++++++---- GPU/Vulkan/TextureCacheVulkan.cpp | 16 ++++++++------- ext/native/thin3d/GLQueueRunner.cpp | 21 +++++++++++++++++++ ext/native/thin3d/GLQueueRunner.h | 2 +- ext/native/thin3d/GLRenderManager.cpp | 16 +++++++++++++++ ext/native/thin3d/GLRenderManager.h | 2 +- 7 files changed, 81 insertions(+), 19 deletions(-) diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 425d8b738b..fc12d417db 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -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; diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 8560438e04..39b3c7ede1 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -907,19 +907,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 diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 4b844b192b..041ed911e8 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -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; diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 381934224b..d7a724ae25 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -947,7 +947,28 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) { } void GLQueueRunner::PerformReadbackImage(const GLRStep &pass) { + GLRTexture *tex = pass.readback_image.texture; + glBindTexture(GL_TEXTURE_2D, tex->texture); + + CHECK_GL_ERROR_IF_DEBUG(); + + int pixelStride = pass.readback_image.srcRect.w; + glPixelStorei(GL_PACK_ALIGNMENT, 4); + + GLRect2D rect = pass.readback.srcRect; + + int size = 4 * rect.w * rect.h; + if (size > readbackBufferSize_) { + delete[] readbackBuffer_; + readbackBuffer_ = new uint8_t[size]; + readbackBufferSize_ = size; + } + + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glGetTexImage(GL_TEXTURE_2D, pass.readback_image.mipLevel, GL_RGBA, GL_UNSIGNED_BYTE, readbackBuffer_); + + CHECK_GL_ERROR_IF_DEBUG(); } void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) { diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index a4175405f2..362d3e30f3 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -301,7 +301,7 @@ struct GLRStep { Draw::DataFormat dstFormat; } readback; struct { - GLint texture; + GLRTexture *texture; GLRect2D srcRect; int mipLevel; } readback_image; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 63c9ecbb8f..33990e3db1 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -276,6 +276,22 @@ bool GLRenderManager::CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspec 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"); diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 345b8ad6f2..b8933ed074 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -313,7 +313,7 @@ public: 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(GLuint texture, int mipLevel, 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); From 9945e011c6304b0b7e93733b261766640c77d61e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 19 Jan 2018 23:03:08 -0800 Subject: [PATCH 61/96] GLES: Avoid a shutdown race condition. Also handle DeviceLost before shutdown better. --- GPU/GLES/DrawEngineGLES.cpp | 10 +++++++++- Windows/EmuThread.cpp | 5 ++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index d5cf5ae824..68257d3dce 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -172,18 +172,26 @@ void DrawEngineGLES::InitDeviceObjects() { } void DrawEngineGLES::DestroyDeviceObjects() { + // 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(); - render_->DeleteInputLayout(softwareInputLayout_); + if (softwareInputLayout_) + render_->DeleteInputLayout(softwareInputLayout_); + softwareInputLayout_ = nullptr; } void DrawEngineGLES::ClearInputLayoutMap() { diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index da187068d6..c48094bf79 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -105,9 +105,8 @@ void RenderThreadFunc() { } g_graphicsContext->ThreadStart(); - while (emuThreadState != THREAD_SHUTDOWN) { - if (!g_graphicsContext->ThreadFrame()) - break; + while (g_graphicsContext->ThreadFrame()) { + continue; } g_graphicsContext->ThreadEnd(); g_graphicsContext->ShutdownFromRenderThread(); From ccdb4d186d9d82dc7979c4aadb6e91edb4560f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 20 Jan 2018 00:05:59 +0100 Subject: [PATCH 62/96] gl-render-manager: Fix HW tesselation. Remove 1D texture support, likely no benefit. --- GPU/Common/DrawEngineCommon.h | 1 + GPU/GLES/DrawEngineGLES.cpp | 132 +++++++------------------ GPU/GLES/DrawEngineGLES.h | 10 +- GPU/GLES/GPU_GLES.cpp | 3 +- GPU/GLES/ShaderManagerGLES.cpp | 12 +-- GPU/GLES/TextureCacheGLES.cpp | 6 -- GPU/GLES/VertexShaderGeneratorGLES.cpp | 19 ++-- UI/GameSettingsScreen.cpp | 15 +-- ext/native/gfx_es2/gpu_features.cpp | 2 + ext/native/gfx_es2/gpu_features.h | 2 + ext/native/thin3d/GLQueueRunner.cpp | 18 ++-- ext/native/thin3d/GLQueueRunner.h | 1 - 12 files changed, 80 insertions(+), 141 deletions(-) diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index 961e5dad5e..24599a094b 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -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; }; diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 68257d3dce..77717aea19 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -133,7 +133,7 @@ DrawEngineGLES::DrawEngineGLES(Draw::DrawContext *draw) : vai_(256), draw_(draw) InitDeviceObjects(); - tessDataTransfer = new TessellationDataTransferGLES(gl_extensions.VersionGEThan(3, 0, 0)); + tessDataTransfer = new TessellationDataTransferGLES(render_); } DrawEngineGLES::~DrawEngineGLES() { @@ -211,6 +211,7 @@ void DrawEngineGLES::EndFrame() { FrameData &frameData = frameData_[render_->GetCurFrame()]; frameData.pushIndex->End(); frameData.pushVertex->End(); + tessDataTransfer->EndFrame(); } struct GlTypeInfo { @@ -761,7 +762,6 @@ rotateVBO: #ifndef MOBILE_DEVICE host->GPUNotifyDraw(); #endif - CHECK_GL_ERROR_IF_DEBUG(); } bool DrawEngineGLES::IsCodePtrVertexDecoder(const u8 *ptr) const { @@ -769,103 +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) { - // TODO: Implement with the render manager - /* -#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(); - */ } diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index ea75d346e6..7fa9f1e816 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -202,10 +202,14 @@ private: class TessellationDataTransferGLES : public TessellationDataTransfer { private: GLRTexture *data_tex[3]{}; - bool isAllowTexture1D_; + GLRenderManager *renderManager_; public: - TessellationDataTransferGLES(bool isAllowTexture1D) : TessellationDataTransfer(), isAllowTexture1D_(isAllowTexture1D) { } - ~TessellationDataTransferGLES() { } + TessellationDataTransferGLES(GLRenderManager *renderManager) + : renderManager_(renderManager) { } + ~TessellationDataTransferGLES() { + 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. }; }; diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 7887292baa..5417a24a87 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -316,8 +316,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; diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 4c10e61527..a33ce2b80c 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -168,12 +168,12 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, availableUniforms = vs->GetUniformMask() | fs->GetUniformMask(); std::vector 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, 4 }); // Texture unit 4 - initialize.push_back({ &u_tess_tex_tex, 5 }); // Texture unit 5 - initialize.push_back({ &u_tess_col_tex, 6 }); // Texture unit 6 + 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 program = render->CreateProgram(shaders, semantics, queries, initialize, gstate_c.featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND); diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 39b3c7ede1..61e585a8aa 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -115,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; @@ -157,7 +156,6 @@ void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) { 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); - CHECK_GL_ERROR_IF_DEBUG(); } void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight) { @@ -735,8 +733,6 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r bool useUnpack = false; uint8_t *pixelData; - CHECK_GL_ERROR_IF_DEBUG(); - // TODO: only do this once u32 texByteAlign = 1; @@ -794,8 +790,6 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r } } - CHECK_GL_ERROR_IF_DEBUG(); - GLuint components = dstFmt == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA; GLuint components2 = components; diff --git a/GPU/GLES/VertexShaderGeneratorGLES.cpp b/GPU/GLES/VertexShaderGeneratorGLES.cpp index 1517d58643..f9de2d312b 100644 --- a/GPU/GLES/VertexShaderGeneratorGLES.cpp +++ b/GPU/GLES/VertexShaderGeneratorGLES.cpp @@ -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) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 34fec613b6..6b63263d7d 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -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; } diff --git a/ext/native/gfx_es2/gpu_features.cpp b/ext/native/gfx_es2/gpu_features.cpp index c758645fc9..69ffa7c897 100644 --- a/ext/native/gfx_es2/gpu_features.cpp +++ b/ext/native/gfx_es2/gpu_features.cpp @@ -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] = { diff --git a/ext/native/gfx_es2/gpu_features.h b/ext/native/gfx_es2/gpu_features.h index 2f405546b8..b12b406eee 100644 --- a/ext/native/gfx_es2/gpu_features.h +++ b/ext/native/gfx_es2/gpu_features.h @@ -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); }; diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index d7a724ae25..c07085bf70 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -182,8 +182,6 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { InitCreateFramebuffer(step); break; } - case GLRInitStepType::TEXTURE_SUBDATA: - break; case GLRInitStepType::TEXTURE_IMAGE: { GLRTexture *tex = step.texture_image.texture; @@ -193,13 +191,20 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { glBindTexture(tex->target, tex->texture); boundTexture = tex->texture; } + if (!step.texture_image.data) + Crash(); + // For things to show in RenderDoc, need to split into glTexImage2D(..., nullptr) and glTexSubImage. glTexImage2D(tex->target, step.texture_image.level, step.texture_image.internalFormat, step.texture_image.width, step.texture_image.height, 0, step.texture_image.format, step.texture_image.type, step.texture_image.data); delete[] step.texture_image.data; CHECK_GL_ERROR_IF_DEBUG(); - glTexParameteri(tex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST); - glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST); + tex->wrapS = GL_CLAMP_TO_EDGE; + tex->wrapT = GL_CLAMP_TO_EDGE; + tex->magFilter = step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST; + tex->minFilter = step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST; + glTexParameteri(tex->target, GL_TEXTURE_WRAP_S, tex->wrapS); + glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, tex->wrapT); + glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, tex->magFilter); + glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, tex->minFilter); break; } case GLRInitStepType::TEXTURE_FINALIZE: @@ -210,6 +215,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { boundTexture = tex->texture; } glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, step.texture_finalize.maxLevel); + tex->maxLod = step.texture_finalize.maxLevel; if (step.texture_finalize.genMips) { glGenerateMipmap(tex->target); } diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index 362d3e30f3..95d71292f8 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -185,7 +185,6 @@ enum class GLRInitStepType : uint8_t { CREATE_FRAMEBUFFER, TEXTURE_IMAGE, - TEXTURE_SUBDATA, TEXTURE_FINALIZE, BUFFER_SUBDATA, }; From 903bd07d6ed12e7db8b37f0c058eb22ba3beadaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 20 Jan 2018 13:45:32 +0100 Subject: [PATCH 63/96] gl-render-manager: Fix updating sampler modes when texturing from framebuffer --- ext/native/thin3d/GLQueueRunner.cpp | 62 ++++++++++++++++------------- ext/native/thin3d/GLRenderManager.h | 47 ++++++++++------------ 2 files changed, 55 insertions(+), 54 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index c07085bf70..92075a6af9 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -185,7 +185,6 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { case GLRInitStepType::TEXTURE_IMAGE: { GLRTexture *tex = step.texture_image.texture; - _assert_msg_(G3D, tex->canary == GLRTexture::CanaryValue, "Canary has bad value %08x in texture %p", tex->canary, tex); CHECK_GL_ERROR_IF_DEBUG(); if (boundTexture != tex->texture) { glBindTexture(tex->target, tex->texture); @@ -243,21 +242,22 @@ void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) { // Color texture is same everywhere glGenFramebuffers(1, &fbo->handle); - glGenTextures(1, &fbo->color_texture); + glGenTextures(1, &fbo->color_texture.texture); + fbo->color_texture.target = GL_TEXTURE_2D; + fbo->color_texture.maxLod = 0.0f; // Create the surfaces. - glBindTexture(GL_TEXTURE_2D, fbo->color_texture); - 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_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - + glBindTexture(GL_TEXTURE_2D, fbo->color_texture.texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - 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_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + fbo->color_texture.wrapS = GL_CLAMP_TO_EDGE; + fbo->color_texture.wrapT = GL_CLAMP_TO_EDGE; + fbo->color_texture.magFilter = GL_LINEAR; + fbo->color_texture.minFilter = GL_LINEAR; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, fbo->color_texture.wrapS); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, fbo->color_texture.wrapT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, fbo->color_texture.magFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, fbo->color_texture.minFilter); if (gl_extensions.IsGLES) { if (gl_extensions.OES_packed_depth_stencil) { @@ -272,7 +272,7 @@ void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) { // Bind it all together glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture.texture, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); } else { @@ -292,7 +292,7 @@ void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) { // Bind it all together glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture.texture, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_buffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->stencil_buffer); } @@ -306,7 +306,7 @@ void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) { // Bind it all together glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture.texture, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); } @@ -654,7 +654,6 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } if (c.texture.texture) { if (curTex[slot] != c.texture.texture) { - _assert_msg_(G3D, c.texture.texture->canary == GLRTexture::CanaryValue, "Canary has bad value %08x in texture %p", c.texture.texture->canary, c.texture.texture); glBindTexture(c.texture.texture->target, c.texture.texture->texture); curTex[slot] = c.texture.texture; } @@ -672,9 +671,12 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { activeSlot = slot; } if (c.bind_fb_texture.aspect == GL_COLOR_BUFFER_BIT) { - glBindTexture(GL_TEXTURE_2D, c.bind_fb_texture.framebuffer->color_texture); + if (curTex[slot] != &c.bind_fb_texture.framebuffer->color_texture) + glBindTexture(GL_TEXTURE_2D, c.bind_fb_texture.framebuffer->color_texture.texture); + curTex[slot] = &c.bind_fb_texture.framebuffer->color_texture; } else { // TODO: Depth texturing? + curTex[slot] = nullptr; } break; } @@ -866,8 +868,8 @@ void GLQueueRunner::PerformCopy(const GLRStep &step) { switch (step.copy.aspectMask) { case GL_COLOR_BUFFER_BIT: - srcTex = src->color_texture; - dstTex = dst->color_texture; + srcTex = src->color_texture.texture; + dstTex = dst->color_texture.texture; break; case GL_DEPTH_BUFFER_BIT: // TODO: Support depth copies. @@ -1026,16 +1028,22 @@ void GLQueueRunner::fbo_ext_create(const GLRInitStep &step) { // Color texture is same everywhere glGenFramebuffersEXT(1, &fbo->handle); - glGenTextures(1, &fbo->color_texture); + glGenTextures(1, &fbo->color_texture.texture); // Create the surfaces. - glBindTexture(GL_TEXTURE_2D, fbo->color_texture); + glBindTexture(GL_TEXTURE_2D, fbo->color_texture.texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - 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_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + fbo->color_texture.target = GL_TEXTURE_2D; + fbo->color_texture.wrapS = GL_CLAMP_TO_EDGE; + fbo->color_texture.wrapT = GL_CLAMP_TO_EDGE; + fbo->color_texture.magFilter = step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST; + fbo->color_texture.minFilter = step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST; + fbo->color_texture.maxLod = 0.0f; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, fbo->color_texture.wrapS); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, fbo->color_texture.wrapT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, fbo->color_texture.magFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, fbo->color_texture.minFilter); fbo->stencil_buffer = 0; fbo->z_buffer = 0; @@ -1047,7 +1055,7 @@ void GLQueueRunner::fbo_ext_create(const GLRInitStep &step) { // Bind it all together glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->handle); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fbo->color_texture, 0); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fbo->color_texture.texture, 0); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); @@ -1166,6 +1174,4 @@ GLRFramebuffer::~GLRFramebuffer() { glDeleteRenderbuffers(1, &stencil_buffer); #endif } - - glDeleteTextures(1, &color_texture); } diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index b8933ed074..7fc308cf91 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -18,6 +18,26 @@ 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) @@ -29,7 +49,7 @@ public: int numShadows = 1; // TODO: Support this. GLuint handle = 0; - GLuint color_texture = 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; @@ -105,31 +125,6 @@ public: std::unordered_map uniformCache_; }; -class GLRTexture { -public: - ~GLRTexture() { - if (texture) { - glDeleteTextures(1, &texture); - } - canary = 0xd31373d; // deleted - } - enum { - CanaryValue = 0x12345678, - }; - uint32_t canary = CanaryValue; - 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 GLRBuffer { public: GLRBuffer(GLuint target, size_t size) : target_(target), size_((int)size) {} From e5b8d91c9f0c4f5abb6e02fe3cb014a4bc4f4307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 20 Jan 2018 13:55:51 +0100 Subject: [PATCH 64/96] ES2 buildfix - glGetTexImage is not available --- ext/native/thin3d/GLQueueRunner.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 92075a6af9..b7a107bc69 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -961,6 +961,7 @@ void GLQueueRunner::PerformReadbackImage(const GLRStep &pass) { CHECK_GL_ERROR_IF_DEBUG(); +#ifndef USING_GLES2 int pixelStride = pass.readback_image.srcRect.w; glPixelStorei(GL_PACK_ALIGNMENT, 4); @@ -975,6 +976,7 @@ void GLQueueRunner::PerformReadbackImage(const GLRStep &pass) { glPixelStorei(GL_PACK_ALIGNMENT, 4); glGetTexImage(GL_TEXTURE_2D, pass.readback_image.mipLevel, GL_RGBA, GL_UNSIGNED_BYTE, readbackBuffer_); +#endif CHECK_GL_ERROR_IF_DEBUG(); } From 4c927c142185aed7ebd55cf6c484bbe89e473c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 20 Jan 2018 16:57:06 +0100 Subject: [PATCH 65/96] Reintroduce check for GE_LOGIC_COPY --- GPU/GLES/StateMappingGLES.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 0b96bb05e4..65ea0efdc0 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -229,7 +229,8 @@ void DrawEngineGLES::ApplyDrawState(int prim) { #ifndef USING_GLES2 if (gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) { - renderManager->SetLogicOp(gstate.isLogicOpEnabled(), logicOps[gstate.getLogicOp()]); + renderManager->SetLogicOp(gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY, + logicOps[gstate.getLogicOp()]); } #endif } From f6260c75c926bca294f8d13a737cac4d46ea510d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 07:57:38 -0800 Subject: [PATCH 66/96] GLES: Reset blend state on clear. Because now that resets blending parameters. --- GPU/Common/FramebufferCommon.cpp | 12 ++++++++++++ GPU/GLES/FramebufferManagerGLES.cpp | 2 ++ 2 files changed, 14 insertions(+) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 718500058b..3f93329d22 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -609,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 }); } @@ -852,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; } @@ -921,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 { @@ -930,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; } } @@ -1064,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() { @@ -1185,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); } @@ -1195,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) { diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 711c97b398..adf573502f 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -463,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 }); } @@ -557,6 +558,7 @@ void FramebufferManagerGLES::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); } else if (gl_extensions.IsGLES) { draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); + gstate_c.Dirty(DIRTY_BLEND_STATE); } } From 638cbf725b6bf7ebe52f88ca7ccd344055bd5081 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 07:58:04 -0800 Subject: [PATCH 67/96] GLES: Skip blend reset after stencil upload. We dirty the flag anyway. Avoid extra calls. --- GPU/GLES/StencilBufferGLES.cpp | 1 - ext/native/thin3d/GLQueueRunner.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 0f3a19b881..e86aad078f 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -196,7 +196,6 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe draw_->BlitFramebuffer(blitFBO, 0, 0, w, h, dstBuffer->fbo, 0, 0, dstBuffer->renderWidth, dstBuffer->renderHeight, Draw::FB_STENCIL_BIT, Draw::FB_BLIT_NEAREST); } - render_->SetNoBlendAndMask(0xF); gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE); RebindFramebuffer(); return true; diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index b7a107bc69..da262e425d 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -214,7 +214,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { boundTexture = tex->texture; } glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, step.texture_finalize.maxLevel); - tex->maxLod = step.texture_finalize.maxLevel; + tex->maxLod = (float)step.texture_finalize.maxLevel; if (step.texture_finalize.genMips) { glGenerateMipmap(tex->target); } From b566b6cca2c5109bdf3da5da82718d674e249f02 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 08:28:26 -0800 Subject: [PATCH 68/96] D3D11: Remove potentially misleading rebind func. This doesn't override the GPUCommon one, it just shares the same name, which might lead to confusion later. Better not to use the parent. --- GPU/D3D11/FramebufferManagerD3D11.cpp | 9 --------- GPU/D3D11/FramebufferManagerD3D11.h | 1 - 2 files changed, 10 deletions(-) diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 6da4b477c1..c45ec45cd5 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -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; diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index 1bc40e960e..d951d60e7f 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -58,7 +58,6 @@ public: void BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; - void RebindFramebuffer(); // TODO: Remove ID3D11Buffer *GetDynamicQuadBuffer() { From 48a07474f8d938cb0070d9979afb9b2c1529f0fb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 08:35:24 -0800 Subject: [PATCH 69/96] GLES: Avoid using failed depal shaders if possible. Better to have one totally broken frame than all totally broken frames. --- GPU/GLES/DepalettizeShaderGLES.cpp | 6 +++++- GPU/GLES/ShaderManagerGLES.cpp | 1 - ext/native/thin3d/GLQueueRunner.cpp | 1 + ext/native/thin3d/GLRenderManager.h | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/GPU/GLES/DepalettizeShaderGLES.cpp b/GPU/GLES/DepalettizeShaderGLES.cpp index bc3a84297f..49e1b36f3c 100644 --- a/GPU/GLES/DepalettizeShaderGLES.cpp +++ b/GPU/GLES/DepalettizeShaderGLES.cpp @@ -139,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; diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index a33ce2b80c..195f8516a1 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -179,7 +179,6 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, // The rest, use the "dirty" mechanism. dirtyUniforms = DIRTY_ALL_UNIFORMS; - CHECK_GL_ERROR_IF_DEBUG(); } LinkedShader::~LinkedShader() { diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index da262e425d..ec874cf119 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -164,6 +164,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { OutputDebugStringUTF8(infoLog); #endif step.create_shader.shader->valid = false; + step.create_shader.shader->failed = true; } delete[] step.create_shader.code; delete[] step.create_shader.desc; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 7fc308cf91..b98dfa1dbb 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -73,6 +73,8 @@ public: } GLuint shader = 0; bool valid = false; + // Warning: Won't know until a future frame. + bool failed = false; }; class GLRProgram { From 3380ab8705de88bc6ae2646082a623f17ca5e7fa Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 08:46:34 -0800 Subject: [PATCH 70/96] GLES: Keep the GLRShader desc around. This way we can also log it on link errors. It's not much data. --- GPU/GLES/ShaderManagerGLES.cpp | 11 +++++------ GPU/GLES/ShaderManagerGLES.h | 2 +- ext/native/thin3d/GLQueueRunner.cpp | 5 ++--- ext/native/thin3d/GLQueueRunner.h | 3 +-- ext/native/thin3d/GLRenderManager.h | 8 +++----- 5 files changed, 12 insertions(+), 17 deletions(-) diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 195f8516a1..bb06521ea6 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -44,12 +44,11 @@ #include "GPU/GLES/DrawEngineGLES.h" #include "FramebufferManagerGLES.h" -Shader::Shader(GLRenderManager *render, const char *code, const char *desc, uint32_t glShaderType, bool useHWTransform, uint32_t attrMask, uint64_t 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; - std::string descstr(desc); #ifdef SHADERLOG #ifdef _WIN32 OutputDebugStringUTF8(code); @@ -57,7 +56,7 @@ Shader::Shader(GLRenderManager *render, const char *code, const char *desc, uint printf("%s\n", code); #endif #endif - shader = render->CreateShader(glShaderType, source_, descstr); + shader = render->CreateShader(glShaderType, source_, desc); } Shader::~Shader() { @@ -609,7 +608,7 @@ Shader *ShaderManagerGLES::CompileFragmentShader(FShaderID FSID) { return nullptr; } std::string desc = FragmentShaderDesc(FSID); - return new Shader(render_, codeBuffer_, desc.c_str(), GL_FRAGMENT_SHADER, false, 0, uniformMask); + return new Shader(render_, codeBuffer_, desc, GL_FRAGMENT_SHADER, false, 0, uniformMask); } Shader *ShaderManagerGLES::CompileVertexShader(VShaderID VSID) { @@ -618,7 +617,7 @@ Shader *ShaderManagerGLES::CompileVertexShader(VShaderID VSID) { uint64_t uniformMask; GenerateVertexShader(VSID, codeBuffer_, &attrMask, &uniformMask); std::string desc = VertexShaderDesc(VSID); - return new Shader(render_, codeBuffer_, desc.c_str(), GL_VERTEX_SHADER, useHWTransform, attrMask, uniformMask); + return new Shader(render_, codeBuffer_, desc, GL_VERTEX_SHADER, useHWTransform, attrMask, uniformMask); } Shader *ShaderManagerGLES::ApplyVertexShader(int prim, u32 vertType, VShaderID *VSID) { @@ -668,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(render_, codeBuffer_, VertexShaderDesc(vsidTemp).c_str(), GL_VERTEX_SHADER, false, attrMask, uniformMask); + vs = new Shader(render_, codeBuffer_, VertexShaderDesc(vsidTemp), GL_VERTEX_SHADER, false, attrMask, uniformMask); } vsCache_.Insert(*VSID, vs); diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index b7ee34216b..77b94f9cd2 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -125,7 +125,7 @@ public: class Shader { public: - Shader(GLRenderManager *render, const char *code, const char *desc, 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(); GLRShader *shader; diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index ec874cf119..536847fc39 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -156,10 +156,10 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { 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 for: %s", step.create_shader.desc); + ERROR_LOG(G3D, "Error in shader compilation for: %s", step.create_shader.shader->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, step.create_shader.desc, (const char *)code); + Reporting::ReportMessage("Error in shader compilation: info: %s\n%s\n%s", infoLog, step.create_shader.shader->desc.c_str(), (const char *)code); #ifdef SHADERLOG OutputDebugStringUTF8(infoLog); #endif @@ -167,7 +167,6 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { step.create_shader.shader->failed = true; } delete[] step.create_shader.code; - delete[] step.create_shader.desc; step.create_shader.shader->valid = true; break; } diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index 95d71292f8..498177c178 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -199,9 +199,8 @@ struct GLRInitStep { } create_texture; struct { GLRShader *shader; - // These char arrays need to be allocated with new[]. + // This char arrays needs to be allocated with new[]. char *code; - char *desc; // For error logging. GLuint stage; } create_shader; struct { diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index b98dfa1dbb..bbc40210be 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -75,6 +75,7 @@ public: bool valid = false; // Warning: Won't know until a future frame. bool failed = false; + std::string desc; }; class GLRProgram { @@ -227,16 +228,13 @@ public: return step.create_buffer.buffer; } - GLRShader *CreateShader(GLuint stage, std::string code, std::string desc) { + 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); - if (!desc.empty()) { - step.create_shader.desc = desc.size() ? new char[desc.size() + 1] : nullptr; - memcpy(step.create_shader.desc, desc.data(), desc.size() + 1); - } initSteps_.push_back(step); return step.create_shader.shader; } From e56ae322fd626ef6118542d28e816c8201ca7dd0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 08:58:09 -0800 Subject: [PATCH 71/96] GLES: Report errors for link failures. Let's not lose reporting on this, it's often an issue... --- ext/native/thin3d/GLQueueRunner.cpp | 23 ++++++++++++++++++++--- ext/native/thin3d/GLRenderManager.h | 1 + 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 536847fc39..863398d745 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -101,12 +101,27 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { GLint bufLength = 0; glGetProgramiv(program->program, GL_INFO_LOG_LENGTH, &bufLength); if (bufLength) { - char* buf = new char[bufLength]; - glGetProgramInfoLog(program->program, bufLength, NULL, buf); + char *buf = new char[bufLength]; + glGetProgramInfoLog(program->program, bufLength, nullptr, buf); + + // TODO: Could be other than vs/fs. Also, we're assuming order here... + const char *vsDesc = step.create_program.shaders[0]->desc.c_str(); + const char *fsDesc = step.create_program.num_shaders > 1 ? step.create_program.shaders[1]->desc.c_str() : nullptr; + const char *vsCode = step.create_program.shaders[0]->code.c_str(); + const char *fsCode = step.create_program.num_shaders > 1 ? step.create_program.shaders[1]->code.c_str() : nullptr; + Reporting::ReportMessage("Error in shader program link: info: %s\nfs: %s\n%s\nvs: %s\n%s", buf, fsDesc, fsCode, vsDesc, vsCode); + ELOG("Could not link program:\n %s", buf); - // We've thrown out the source at this point. Might want to do something about that. + ERROR_LOG(G3D, "VS desc:\n%s", vsDesc); + ERROR_LOG(G3D, "FS desc:\n%s", fsDesc); + ERROR_LOG(G3D, "VS:\n%s\n", vsCode); + ERROR_LOG(G3D, "FS:\n%s\n", fsCode); + #ifdef _WIN32 OutputDebugStringUTF8(buf); + OutputDebugStringUTF8(vsCode); + if (fsCode) + OutputDebugStringUTF8(fsCode); #endif delete[] buf; } else { @@ -166,6 +181,8 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { step.create_shader.shader->valid = false; step.create_shader.shader->failed = true; } + // Before we throw away the code, attach it to the shader for debugging. + step.create_shader.shader->code = code; delete[] step.create_shader.code; step.create_shader.shader->valid = true; break; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index bbc40210be..90502cbb24 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -76,6 +76,7 @@ public: // Warning: Won't know until a future frame. bool failed = false; std::string desc; + std::string code; }; class GLRProgram { From 38161f3c69de5830943c2307b8b7f3a84f11fbdc Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 09:05:52 -0800 Subject: [PATCH 72/96] GLES: Use linear for high-res FBO tex copies. --- GPU/GLES/FramebufferManagerGLES.cpp | 2 +- GPU/GLES/StateMappingGLES.cpp | 3 +++ ext/native/thin3d/GLQueueRunner.cpp | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index adf573502f..42015c1169 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -494,7 +494,7 @@ void FramebufferManagerGLES::BlitFramebufferDepth(VirtualFramebuffer *src, Virtu void FramebufferManagerGLES::BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags) { if (!framebuffer->fbo || !useBufferedRendering_) { - render_->BindTexture(0, nullptr); + render_->BindTexture(stage, nullptr); gstate_c.skipDrawReason |= SKIPDRAW_BAD_FB_TEXTURE; return; } diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 65ea0efdc0..a16f7a4e82 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -319,6 +319,9 @@ void DrawEngineGLES::ApplyDrawStateLate(bool setStencil, int stencilValue) { // Note that this is positions, not UVs, that we need the copy from. framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); framebufferManager_->RebindFramebuffer(); + GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + // 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; } diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 863398d745..7fc9cb5fdf 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -1056,8 +1056,8 @@ void GLQueueRunner::fbo_ext_create(const GLRInitStep &step) { fbo->color_texture.target = GL_TEXTURE_2D; fbo->color_texture.wrapS = GL_CLAMP_TO_EDGE; fbo->color_texture.wrapT = GL_CLAMP_TO_EDGE; - fbo->color_texture.magFilter = step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST; - fbo->color_texture.minFilter = step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST; + fbo->color_texture.magFilter = GL_LINEAR; + fbo->color_texture.minFilter = GL_LINEAR; fbo->color_texture.maxLod = 0.0f; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, fbo->color_texture.wrapS); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, fbo->color_texture.wrapT); From 2740f74cc48b2a6f10abfab167cba4b2ef48c7b9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 09:09:40 -0800 Subject: [PATCH 73/96] GLES: Add TODO note for shader blend texture. --- GPU/GLES/StateMappingGLES.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index a16f7a4e82..b8a17baf9e 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -317,6 +317,7 @@ void DrawEngineGLES::ApplyDrawStateLate(bool setStencil, int stencilValue) { if (!gstate.isModeClear()) { 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); framebufferManager_->RebindFramebuffer(); GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); From 900e53b6dc12e9f71ea709c44c57b9216fe07cc0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 09:17:25 -0800 Subject: [PATCH 74/96] GLES: Re-enable texture scaling. --- GPU/GLES/TextureCacheGLES.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 61e585a8aa..845036945c 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -771,9 +771,12 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r entry.SetAlphaStatus(TexCacheEntry::STATUS_ALPHA_UNKNOWN); } - // TODO: Scale's buffer management doesn't work. - //if (scaleFactor > 1) - // scaler.Scale((uint32_t *)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; From acc3e39b67cb05ef53f0d2694739bbe068b8b926 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 09:35:43 -0800 Subject: [PATCH 75/96] GLES: Reintroduce out of memory checks. --- GPU/GLES/TextureCacheGLES.cpp | 18 +++++++++++++----- ext/native/thin3d/GLQueueRunner.cpp | 21 +++++++++++++++++++++ ext/native/thin3d/GLQueueRunner.h | 6 ++++++ ext/native/thin3d/GLRenderManager.h | 4 ++++ 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 845036945c..59eb3184c1 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -207,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_); } @@ -803,11 +816,6 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r // 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()) render_->TextureImage(entry.textureName, 0, w, h, components, components2, dstFmt, pixelData); else diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 7fc9cb5fdf..83820ff7e4 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -23,6 +23,9 @@ GLuint g_defaultFBO = 0; void GLQueueRunner::CreateDeviceObjects() { glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyLevel_); glGenVertexArrays(1, &globalVAO_); + + // An eternal optimist. + sawOutOfMemory_ = false; } void GLQueueRunner::DestroyDeviceObjects() { @@ -36,6 +39,7 @@ void GLQueueRunner::DestroyDeviceObjects() { void GLQueueRunner::RunInitSteps(const std::vector &steps) { glActiveTexture(GL_TEXTURE0); GLuint boundTexture = (GLuint)-1; + bool allocatedTextures = false; for (int i = 0; i < steps.size(); i++) { const GLRInitStep &step = steps[i]; @@ -197,6 +201,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { { boundTexture = (GLuint)-1; InitCreateFramebuffer(step); + allocatedTextures = true; break; } case GLRInitStepType::TEXTURE_IMAGE: @@ -211,6 +216,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { Crash(); // For things to show in RenderDoc, need to split into glTexImage2D(..., nullptr) and glTexSubImage. glTexImage2D(tex->target, step.texture_image.level, step.texture_image.internalFormat, step.texture_image.width, step.texture_image.height, 0, step.texture_image.format, step.texture_image.type, step.texture_image.data); + allocatedTextures = true; delete[] step.texture_image.data; CHECK_GL_ERROR_IF_DEBUG(); tex->wrapS = GL_CLAMP_TO_EDGE; @@ -242,6 +248,21 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { break; } } + + // TODO: Use GL_KHR_no_error or a debug callback, where supported? + if (allocatedTextures) { + // Users may use replacements or scaling, with high render resolutions, and run out of VRAM. + // This detects that, rather than looking like PPSSPP is broken. + // Calling glGetError() isn't great, but at the end of init shouldn't be too bad... + GLenum err = glGetError(); + if (err == GL_OUT_OF_MEMORY) { + WARN_LOG_REPORT(G3D, "GL ran out of GPU memory; switching to low memory mode"); + sawOutOfMemory_ = true; + } 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 after init: %08x (%s)", err, GLEnumToString(err).c_str()); + } + } } void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) { diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index 498177c178..e04f2f495e 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -328,6 +328,10 @@ public: targetWidth_ = width; targetHeight_ = height; } + + bool SawOutOfMemory() { + return sawOutOfMemory_; + } private: void InitCreateFramebuffer(const GLRInitStep &step); @@ -374,4 +378,6 @@ private: GLuint AllocTextureName(); // Texture name cache. Ripped straight from TextureCacheGLES. std::vector nameCache_; + + bool sawOutOfMemory_ = false; }; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 90502cbb24..abc4fb7ced 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -653,6 +653,10 @@ public: void StopThread(); + bool SawOutOfMemory() { + return queueRunner_.SawOutOfMemory(); + } + private: void BeginSubmitFrame(int frame); void EndSubmitFrame(int frame); From 30a60018a0aa44c3a4f550e2cefa5e311a5c1d33 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 12:05:30 -0800 Subject: [PATCH 76/96] GLES: Fix race crash on shutdown. This happens when there are pointers in step commands that get freed. --- GPU/GLES/GPU_GLES.cpp | 4 ++++ ext/native/thin3d/GLRenderManager.cpp | 27 +++++++++++++++++++++++++++ ext/native/thin3d/GLRenderManager.h | 4 ++++ 3 files changed, 35 insertions(+) diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 5417a24a87..f58c47ddb7 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -184,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(); diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 33990e3db1..d272f2571b 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -148,10 +148,12 @@ void GLRenderManager::StopThread() { 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 lock(frameData.push_mutex); while (!frameData.readyForFence) { @@ -306,6 +308,7 @@ void GLRenderManager::BeginFrame() { frameData.push_condVar.wait(lock); } frameData.readyForFence = false; + frameData.readyForSubmit = true; } VLOG("PUSH: Fencing %d", curFrame); @@ -381,7 +384,9 @@ void GLRenderManager::Submit(int frame, bool triggerFence) { VLOG("PULL: Frame %d.readyForFence = true", frame); std::unique_lock lock(frameData.push_mutex); + assert(frameData.readyForSubmit); frameData.readyForFence = true; + frameData.readyForSubmit = false; frameData.push_condVar.notify_all(); } } @@ -466,6 +471,7 @@ void GLRenderManager::FlushSync() { frameData.push_condVar.wait(lock); } frameData.readyForFence = false; + frameData.readyForSubmit = true; } } @@ -484,17 +490,38 @@ void GLRenderManager::EndSyncFrame(int frame) { if (useThread_) { std::unique_lock 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 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); diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index abc4fb7ced..703cda773f 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -211,6 +211,9 @@ 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(); + // 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 }; @@ -676,6 +679,7 @@ private: bool readyForFence = true; bool readyForRun = false; + bool readyForSubmit = false; bool skipSwap = false; GLRRunType type = GLRRunType::END; From 7f0b35cd23684fd868ecb6a1c45a2763021f3b81 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 16:44:36 -0800 Subject: [PATCH 77/96] GLES: Run ThreadFrame until frame complete. This makes it easier for frontends that need to call this between other things to handle vsync, or etc. --- ext/native/thin3d/GLRenderManager.cpp | 54 ++++++++++++++------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index d272f2571b..2792e76d1a 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -79,38 +79,42 @@ bool GLRenderManager::ThreadFrame() { std::unique_lock 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 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. + { + std::unique_lock 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_); + // 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; } From e280cbbc3cc541e9afced4afb233d8daab16e3f2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 22:56:18 -0800 Subject: [PATCH 78/96] GLES: Correct shader blending. Lunar's spell effects were blending completely wrong, and are a good example of shader blending. This fixes them. --- GPU/GLES/StateMappingGLES.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index b8a17baf9e..caa6ad2472 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -173,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(); @@ -315,18 +330,6 @@ void DrawEngineGLES::ApplyDrawStateLate(bool setStencil, int stencilValue) { // 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_) { - // 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); - framebufferManager_->RebindFramebuffer(); - GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - // 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; - } - // Apply last, once we know the alpha params of the texture. if (gstate.isAlphaTestEnabled() || gstate.isColorTestEnabled()) { fragmentTestCache_->BindTestTexture(2); From 22f65500f1d65da8d96317c69a981e505cdbf1b5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 23:05:28 -0800 Subject: [PATCH 79/96] GPU: Fix shader blending recopying. This at least didn't look horrible like GLEs did, but still looked odd in Lunar. --- GPU/D3D11/StateMappingD3D11.cpp | 4 ++++ GPU/Vulkan/DrawEngineVulkan.cpp | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index 002a118fa4..ee76b37322 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -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); } diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index a11e02d27e..6143778ddd 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -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; From 0399088fc772b4b314b4904ad14d81321b8f7e81 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 21 Jan 2018 08:49:34 -0800 Subject: [PATCH 80/96] GLES: Handle glGetString() on GL thread. We only use a few strings, so should be fine to cache them. --- GPU/GLES/GPU_GLES.cpp | 26 ++++++++++---------------- ext/native/thin3d/GLQueueRunner.cpp | 14 ++++++++++++++ ext/native/thin3d/GLQueueRunner.h | 8 ++++++++ ext/native/thin3d/GLRenderManager.h | 5 +++++ ext/native/thin3d/thin3d_gl.cpp | 8 ++++---- 5 files changed, 41 insertions(+), 20 deletions(-) diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index f58c47ddb7..4ca8419772 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -365,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; diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 83820ff7e4..a7072931bb 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -26,6 +26,20 @@ void GLQueueRunner::CreateDeviceObjects() { // An eternal optimist. sawOutOfMemory_ = false; + + // Populate some strings from the GL thread. + auto populate = [&](int name) { + const GLubyte *value = glGetString(name); + if (!value) + glStrings_[name] = "?"; + else + glStrings_[name] = (const char *)value; + }; + populate(GL_VENDOR); + populate(GL_RENDERER); + populate(GL_VERSION); + populate(GL_SHADING_LANGUAGE_VERSION); + populate(GL_EXTENSIONS); } void GLQueueRunner::DestroyDeviceObjects() { diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index e04f2f495e..511e3425a1 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -2,6 +2,7 @@ #include #include +#include #include "gfx/gl_common.h" #include "thin3d/DataFormat.h" @@ -332,6 +333,12 @@ public: 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); @@ -378,6 +385,7 @@ private: GLuint AllocTextureName(); // Texture name cache. Ripped straight from TextureCacheGLES. std::vector nameCache_; + std::unordered_map glStrings_; bool sawOutOfMemory_ = false; }; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 703cda773f..6bd2d7caf1 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -660,6 +660,11 @@ public: 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); diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index a16411fbe7..b831e199a0 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -435,7 +435,7 @@ public: } else { return "OpenGL"; } - case VENDORSTRING: return (const char *)glGetString(GL_VENDOR); + case VENDORSTRING: return renderManager_.GetGLString(GL_VENDOR); case VENDOR: switch (caps_.vendor) { case GPUVendor::VENDOR_AMD: return "VENDOR_AMD"; @@ -450,9 +450,9 @@ public: return "VENDOR_UNKNOWN"; } break; - case DRIVER: return (const char *)glGetString(GL_RENDERER); - case SHADELANGVERSION: return (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION); - case APIVERSION: return (const char *)glGetString(GL_VERSION); + case DRIVER: return renderManager_.GetGLString(GL_RENDERER); + case SHADELANGVERSION: return renderManager_.GetGLString(GL_SHADING_LANGUAGE_VERSION); + case APIVERSION: return renderManager_.GetGLString(GL_VERSION); default: return "?"; } } From 59904c316bf33b148f8d66c88d05ee5c20ec07b8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 21 Jan 2018 09:03:19 -0800 Subject: [PATCH 81/96] SDL: Allow running GL on thread. Tested on a Mac. --- SDL/SDLMain.cpp | 86 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 6 deletions(-) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index acc5cc0e10..7770682204 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -14,9 +14,11 @@ SDLJoystick *joystick = NULL; #include #endif +#include #include #include #include +#include #include "base/display.h" #include "base/logging.h" @@ -32,6 +34,7 @@ SDLJoystick *joystick = NULL; #include "util/text/utf8.h" #include "util/text/parsers.h" #include "math/math_util.h" +#include "thin3d/GLRenderManager.h" #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanDebug.h" #include "math.h" @@ -198,6 +201,8 @@ void EGL_Close() { } #endif +class GLRenderManager; + class SDLGLGraphicsContext : public DummyGraphicsContext { public: SDLGLGraphicsContext() { @@ -218,21 +223,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 +354,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 +713,38 @@ void ToggleFullScreenIfFlagSet(SDL_Window *window) { } } +enum class EmuThreadState { + DISABLED, + START_REQUESTED, + RUNNING, + QUIT_REQUESTED, + STOPPED, +}; + +static std::thread emuThread; +static std::atomic emuThreadState((int)EmuThreadState::DISABLED); + +static void EmuThreadFunc() { + // 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 +942,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 +993,9 @@ int main(int argc, char *argv[]) { int framecount = 0; bool mouseDown = false; + EmuThreadStart(); + graphicsContext->ThreadStart(); + while (true) { SDL_Event event; while (SDL_PollEvent(&event)) { @@ -1101,7 +1163,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 +1182,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(); From 95bcda409d7b6ff4e538801f8717ac2c77bcc803 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 21 Jan 2018 09:17:24 -0800 Subject: [PATCH 82/96] GLES: Fix segfault on GL 2.x. --- ext/native/thin3d/GLQueueRunner.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index a7072931bb..a895ed4649 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -22,7 +22,9 @@ GLuint g_defaultFBO = 0; void GLQueueRunner::CreateDeviceObjects() { glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyLevel_); - glGenVertexArrays(1, &globalVAO_); + if (gl_extensions.ARB_vertex_array_object) { + glGenVertexArrays(1, &globalVAO_); + } // An eternal optimist. sawOutOfMemory_ = false; @@ -47,7 +49,9 @@ void GLQueueRunner::DestroyDeviceObjects() { glDeleteTextures((GLsizei)nameCache_.size(), &nameCache_[0]); nameCache_.clear(); } - glDeleteVertexArrays(1, &globalVAO_); + if (gl_extensions.ARB_vertex_array_object) { + glDeleteVertexArrays(1, &globalVAO_); + } } void GLQueueRunner::RunInitSteps(const std::vector &steps) { @@ -479,7 +483,9 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { #endif */ - glBindVertexArray(globalVAO_); + if (gl_extensions.ARB_vertex_array_object) { + glBindVertexArray(globalVAO_); + } GLRFramebuffer *fb = step.render.framebuffer; GLRProgram *curProgram = nullptr; @@ -889,7 +895,9 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { // Wipe out the current state. glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindVertexArray(0); + if (gl_extensions.ARB_vertex_array_object) { + glBindVertexArray(0); + } glDisable(GL_SCISSOR_TEST); glDisable(GL_DEPTH_TEST); glDisable(GL_STENCIL_TEST); From 062566b67cb0822710e69b8b6f3ab90a62d90eff Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 21 Jan 2018 09:47:07 -0800 Subject: [PATCH 83/96] Core: Set thread names when possible. This doesn't seem to be working for lldb, though. --- SDL/SDLMain.cpp | 3 +++ ext/native/thread/threadutil.cpp | 8 +++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 7770682204..38fcfc4203 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -35,6 +35,7 @@ SDLJoystick *joystick = NULL; #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" @@ -725,6 +726,8 @@ static std::thread emuThread; static std::atomic 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; diff --git a/ext/native/thread/threadutil.cpp b/ext/native/thread/threadutil.cpp index fcd050e98f..403f8cadd9 100644 --- a/ext/native/thread/threadutil.cpp +++ b/ext/native/thread/threadutil.cpp @@ -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 #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 From 56a18907ddf29c56db65f38a75c21afcace78b32 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 21 Jan 2018 11:02:22 -0800 Subject: [PATCH 84/96] SDL: Oops, only start the emu thread for GLES. Probably would've caused issues (at least weird vsync) for Vulkan. --- SDL/SDLMain.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 38fcfc4203..a3a15581e0 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -996,7 +996,9 @@ int main(int argc, char *argv[]) { int framecount = 0; bool mouseDown = false; - EmuThreadStart(); + if (GetGPUBackend() == GPUBackend::OPENGL) { + EmuThreadStart(); + } graphicsContext->ThreadStart(); while (true) { From 0943cf3fcc246215b4d15438733064d69448d054 Mon Sep 17 00:00:00 2001 From: Kentucky Compass Date: Mon, 22 Jan 2018 00:15:41 -0800 Subject: [PATCH 85/96] use GLRenderManager on iOS --- ios/ViewController.mm | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 2ec8d7e7a7..b381b15bb3 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -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,7 @@ static GraphicsContext *graphicsContext; self.gameController = nil; } #endif + graphicsContext->ThreadEnd(); NativeShutdownGraphics(); graphicsContext->Shutdown(); delete graphicsContext; @@ -216,9 +241,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 From 49c5880fccec11210977229d2b00650f84b4d68c Mon Sep 17 00:00:00 2001 From: Kentucky Compass Date: Mon, 22 Jan 2018 00:25:34 -0800 Subject: [PATCH 86/96] disable the iOS thread before shutting down --- ios/ViewController.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/ios/ViewController.mm b/ios/ViewController.mm index b381b15bb3..d8b6d79f12 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -219,6 +219,7 @@ static GraphicsContext *graphicsContext; self.gameController = nil; } #endif + threadEnabled = false; graphicsContext->ThreadEnd(); NativeShutdownGraphics(); graphicsContext->Shutdown(); From d43283239d4339a13368b988f747421609c07166 Mon Sep 17 00:00:00 2001 From: Kentucky Compass Date: Tue, 23 Jan 2018 01:28:16 -0800 Subject: [PATCH 87/96] whitespace cleanup --- ios/ViewController.mm | 48 +++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/ios/ViewController.mm b/ios/ViewController.mm index d8b6d79f12..050202ec95 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -47,20 +47,20 @@ public: Draw::DrawContext *GetDrawContext() override { return draw_; } - void ThreadStart() override { - renderManager_->ThreadStart(); - } - - bool ThreadFrame() override { - return renderManager_->ThreadFrame(); - } - - void ThreadEnd() override { - renderManager_->ThreadEnd(); - } + void ThreadStart() override { + renderManager_->ThreadStart(); + } + + bool ThreadFrame() override { + return renderManager_->ThreadFrame(); + } + + void ThreadEnd() override { + renderManager_->ThreadEnd(); + } private: Draw::DrawContext *draw_; - GLRenderManager *renderManager_; + GLRenderManager *renderManager_; }; static float dp_xscale = 1.0f; @@ -169,7 +169,7 @@ static GraphicsContext *graphicsContext; pixel_in_dps_y = (float)pixel_yres / (float)dp_yres; graphicsContext = new IOSGraphicsContext(); - + NativeInitGraphics(graphicsContext); graphicsContext->ThreadStart(); @@ -188,14 +188,14 @@ static GraphicsContext *graphicsContext; } } #endif - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ - while (threadEnabled) { - NativeUpdate(); - NativeRender(graphicsContext); - time_update(); - } - }); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + while (threadEnabled) { + NativeUpdate(); + NativeRender(graphicsContext); + time_update(); + } + }); } - (void)didReceiveMemoryWarning @@ -219,8 +219,8 @@ static GraphicsContext *graphicsContext; self.gameController = nil; } #endif - threadEnabled = false; - graphicsContext->ThreadEnd(); + threadEnabled = false; + graphicsContext->ThreadEnd(); NativeShutdownGraphics(); graphicsContext->Shutdown(); delete graphicsContext; @@ -242,7 +242,7 @@ static GraphicsContext *graphicsContext; - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { - graphicsContext->ThreadFrame(); + graphicsContext->ThreadFrame(); } - (void)touchX:(float)x y:(float)y code:(int)code pointerId:(int)pointerId From c5a09de446c9cb299f7ef27879b956b329ad2404 Mon Sep 17 00:00:00 2001 From: Kentucky Compass Date: Tue, 23 Jan 2018 01:30:58 -0800 Subject: [PATCH 88/96] one more whitespace fix --- ios/ViewController.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 050202ec95..064a89df33 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -36,7 +36,7 @@ public: IOSGraphicsContext() { CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); - renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); SetGPUBackend(GPUBackend::OPENGL); bool success = draw_->CreatePresets(); assert(success); From e7c9afd7bbc5934570b629d702f6ae735715d305 Mon Sep 17 00:00:00 2001 From: Kentucky Compass Date: Tue, 23 Jan 2018 01:33:02 -0800 Subject: [PATCH 89/96] another whitespace fix --- ios/ViewController.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 064a89df33..a214286791 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -171,7 +171,7 @@ static GraphicsContext *graphicsContext; graphicsContext = new IOSGraphicsContext(); NativeInitGraphics(graphicsContext); - graphicsContext->ThreadStart(); + graphicsContext->ThreadStart(); dp_xscale = (float)dp_xres / (float)pixel_xres; dp_yscale = (float)dp_yres / (float)pixel_yres; From 7abb8702ce562b6e6e67fd1f3f0c72c8478491a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 20 Jan 2018 20:29:18 +0100 Subject: [PATCH 90/96] Work towards threaded GL on Android. Now TextDrawer barfs on wrong JNI context. --- android/jni/AndroidEGLContext.cpp | 7 +- android/jni/AndroidEGLContext.h | 2 +- android/jni/AndroidGraphicsContext.h | 4 +- android/jni/AndroidJavaGLContext.cpp | 21 ++- android/jni/AndroidJavaGLContext.h | 29 +++- android/jni/AndroidVulkanContext.cpp | 7 +- android/jni/AndroidVulkanContext.h | 2 +- android/jni/app-android.cpp | 148 +++++++++++++----- .../org/ppsspp/ppsspp/NativeSurfaceView.java | 2 +- 9 files changed, 172 insertions(+), 50 deletions(-) diff --git a/android/jni/AndroidEGLContext.cpp b/android/jni/AndroidEGLContext.cpp index 8742085535..c9b5d81251 100644 --- a/android/jni/AndroidEGLContext.cpp +++ b/android/jni/AndroidEGLContext.cpp @@ -6,13 +6,18 @@ #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... diff --git a/android/jni/AndroidEGLContext.h b/android/jni/AndroidEGLContext.h index efeafecf80..fd86bc6cfe 100644 --- a/android/jni/AndroidEGLContext.h +++ b/android/jni/AndroidEGLContext.h @@ -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 {} diff --git a/android/jni/AndroidGraphicsContext.h b/android/jni/AndroidGraphicsContext.h index 027be92505..72279b7d6d 100644 --- a/android/jni/AndroidGraphicsContext.h +++ b/android/jni/AndroidGraphicsContext.h @@ -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) {} }; diff --git a/android/jni/AndroidJavaGLContext.cpp b/android/jni/AndroidJavaGLContext.cpp index 8a97a62187..70dc543a4c 100644 --- a/android/jni/AndroidJavaGLContext.cpp +++ b/android/jni/AndroidJavaGLContext.cpp @@ -4,16 +4,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 } diff --git a/android/jni/AndroidJavaGLContext.h b/android/jni/AndroidJavaGLContext.h index 36e443e51d..c6e11910e7 100644 --- a/android/jni/AndroidJavaGLContext.h +++ b/android/jni/AndroidJavaGLContext.h @@ -1,14 +1,25 @@ #pragma once +#include +#include +#include + #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; }; diff --git a/android/jni/AndroidVulkanContext.cpp b/android/jni/AndroidVulkanContext.cpp index 99b21469be..29ad3f3103 100644 --- a/android/jni/AndroidVulkanContext.cpp +++ b/android/jni/AndroidVulkanContext.cpp @@ -58,7 +58,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 @@ -73,7 +74,9 @@ 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(); diff --git a/android/jni/AndroidVulkanContext.h b/android/jni/AndroidVulkanContext.h index 8c48e09c92..e43041d3dc 100644 --- a/android/jni/AndroidVulkanContext.h +++ b/android/jni/AndroidVulkanContext.h @@ -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; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 97ce69edc4..819d88468c 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,21 @@ 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 emuThreadState((int)EmuThreadState::DISABLED); + +void UpdateRunLoopAndroid(JNIEnv *env); + static AndroidAudioState *g_audioState; struct FrameCommand { @@ -114,7 +130,33 @@ static bool javaGL = true; static std::string library_path; static std::map permissions; -GraphicsContext *graphicsContext; +AndroidGraphicsContext *graphicsContext; + +static void EmuThreadFunc(JavaVM *vm) { + JNIEnv *env; + vm->AttachCurrentThread(&env, nullptr); + // 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; + vm->DetachCurrentThread(); +} + +static void EmuThreadStart(JNIEnv *env) { + emuThreadState = (int)EmuThreadState::START_REQUESTED; + JavaVM *vm; + env->GetJavaVM(&vm); + emuThread = std::thread(&EmuThreadFunc, vm); +} + +static void EmuThreadStop() { + emuThreadState = (int)EmuThreadState::QUIT_REQUESTED; + emuThread.join(); + emuThread = std::thread(); +} static void ProcessFrameCommands(JNIEnv *env); @@ -225,7 +267,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) { @@ -323,8 +364,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 +372,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 +448,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; @@ -407,26 +478,26 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, ELOG("GetEnv failed: %d", res); } - if (javaGL && !graphicsContext) { - graphicsContext = new AndroidJavaEGLGraphicsContext(); - } else if (!graphicsContext) { - _assert_msg_(G3D, false, "No graphics context in displayInit?"); - } + if (!graphicsContext) + Crash(); + // 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"); + graphicsContext->InitFromRenderThread(nullptr, 0, 0, 0, 0); + graphicsContext->ThreadStart(); NativeInitGraphics(graphicsContext); renderer_inited = true; } - NativeMessageReceived("recreateviews", ""); } @@ -470,23 +541,19 @@ 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); + } } - 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. - } + NativeRender(graphicsContext); + time_update(); std::lock_guard guard(frameCommandLock); if (!nativeActivity) { @@ -498,6 +565,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: @@ -762,6 +844,8 @@ 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; @@ -787,14 +871,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)) { diff --git a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java index 2d53328573..1e13dba115 100644 --- a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java +++ b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java @@ -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. } } From bd3a681fd3c05ce78a276f93800bf573d8c3b33c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 28 Jan 2018 17:27:23 +0100 Subject: [PATCH 91/96] Enough JNI/threading hackery to get it to run on Android! Broken readbacks make it crash alot though. --- android/jni/AndroidGraphicsContext.h | 2 +- android/jni/app-android.cpp | 76 ++++++++++++++---------- android/jni/app-android.h | 5 +- ext/native/gfx_es2/draw_text_android.cpp | 4 +- ext/native/thin3d/DataFormatGL.cpp | 2 +- 5 files changed, 52 insertions(+), 37 deletions(-) diff --git a/android/jni/AndroidGraphicsContext.h b/android/jni/AndroidGraphicsContext.h index 72279b7d6d..589cddc97d 100644 --- a/android/jni/AndroidGraphicsContext.h +++ b/android/jni/AndroidGraphicsContext.h @@ -21,5 +21,5 @@ class AndroidGraphicsContext : public GraphicsContext { public: // 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) {} + virtual bool InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) = 0; }; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 819d88468c..2d0731d711 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -51,10 +51,6 @@ #include "app-android.h" -JNIEnv *jniEnvMain; -JNIEnv *jniEnvGraphics; -JavaVM *javaVM; - bool useCPUThread = true; enum class EmuThreadState { @@ -113,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; @@ -132,9 +134,44 @@ static std::map permissions; AndroidGraphicsContext *graphicsContext; -static void EmuThreadFunc(JavaVM *vm) { +JNIEnv* getEnv() { JNIEnv *env; - vm->AttachCurrentThread(&env, nullptr); + 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(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; @@ -142,14 +179,12 @@ static void EmuThreadFunc(JavaVM *vm) { UpdateRunLoopAndroid(env); } emuThreadState = (int)EmuThreadState::STOPPED; - vm->DetachCurrentThread(); + gJvm->DetachCurrentThread(); } static void EmuThreadStart(JNIEnv *env) { emuThreadState = (int)EmuThreadState::START_REQUESTED; - JavaVM *vm; - env->GetJavaVM(&vm); - emuThread = std::thread(&EmuThreadFunc, vm); + emuThread = std::thread(&EmuThreadFunc); } static void EmuThreadStop() { @@ -306,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. @@ -472,15 +504,6 @@ 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 (!graphicsContext) - Crash(); - // We should be running on the render thread here. std::string errorMessage; if (renderer_inited) { @@ -495,7 +518,6 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, ILOG("NativeApp.displayInit() first time"); graphicsContext->InitFromRenderThread(nullptr, 0, 0, 0, 0); graphicsContext->ThreadStart(); - NativeInitGraphics(graphicsContext); renderer_inited = true; } NativeMessageReceived("recreateviews", ""); @@ -548,6 +570,7 @@ void UpdateRunLoopAndroid(JNIEnv *env) { while (!renderer_inited) { sleep_ms(20); } + NativeInitGraphics(graphicsContext); } NativeUpdate(); @@ -852,12 +875,6 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J 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) { @@ -922,7 +939,6 @@ retry: graphicsContext = nullptr; renderLoopRunning = false; WLOG("Render loop function exited."); - jniEnvGraphics = nullptr; return true; } diff --git a/android/jni/app-android.h b/android/jni/app-android.h index 04a849b1bc..49d0dab435 100644 --- a/android/jni/app-android.h +++ b/android/jni/app-android.h @@ -6,8 +6,7 @@ #include -extern JNIEnv *jniEnvMain; -extern JNIEnv *jniEnvGraphics; -extern JavaVM *javaVM; +jclass findClass(const char* name); +JNIEnv* getEnv(); #endif diff --git a/ext/native/gfx_es2/draw_text_android.cpp b/ext/native/gfx_es2/draw_text_android.cpp index 68138ee9a4..1bc3bc6639 100644 --- a/ext/native/gfx_es2/draw_text_android.cpp +++ b/ext/native/gfx_es2/draw_text_android.cpp @@ -16,9 +16,9 @@ #include 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(env_->NewGlobalRef(localClass)); ILOG("cls_textRender: %p", cls_textRenderer); if (cls_textRenderer) { diff --git a/ext/native/thin3d/DataFormatGL.cpp b/ext/native/thin3d/DataFormatGL.cpp index 389d6046e8..e2aa7a7a1b 100644 --- a/ext/native/thin3d/DataFormatGL.cpp +++ b/ext/native/thin3d/DataFormatGL.cpp @@ -87,4 +87,4 @@ bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint return true; } -} \ No newline at end of file +} From 6c109abd9e77c85b0ba7c241ece8dacd8e228aa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 28 Jan 2018 18:00:48 +0100 Subject: [PATCH 92/96] Don't crash on missing readback formats. --- ext/native/thin3d/DataFormatGL.cpp | 1 - ext/native/thin3d/GLQueueRunner.cpp | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/native/thin3d/DataFormatGL.cpp b/ext/native/thin3d/DataFormatGL.cpp index e2aa7a7a1b..5d48f15868 100644 --- a/ext/native/thin3d/DataFormatGL.cpp +++ b/ext/native/thin3d/DataFormatGL.cpp @@ -81,7 +81,6 @@ bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint #endif default: - _assert_msg_(G3D, false, "Thin3d GL: Unsupported texture format %d", (int)fmt); return false; } return true; diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index a895ed4649..3ea3636968 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -984,7 +984,8 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) { GLuint type; int alignment; if (!Draw::Thin3DFormatToFormatAndType(pass.readback.dstFormat, internalFormat, format, type, alignment)) { - assert(false); + ELOG("Readback failed - format %d not available", (int)pass.readback.dstFormat); + return; } int pixelStride = pass.readback.srcRect.w; // Apply the correct alignment. From 19ab367591764b27ba585160d6f2c42ed6ce95f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 28 Jan 2018 19:26:42 +0100 Subject: [PATCH 93/96] Fix some accidental extra-indentation --- android/jni/AndroidEGLContext.cpp | 4 +--- android/jni/AndroidVulkanContext.cpp | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/android/jni/AndroidEGLContext.cpp b/android/jni/AndroidEGLContext.cpp index c9b5d81251..4bc86f1a9e 100644 --- a/android/jni/AndroidEGLContext.cpp +++ b/android/jni/AndroidEGLContext.cpp @@ -6,9 +6,7 @@ #include "GL/GLInterface/EGLAndroid.h" #include "Core/System.h" -bool AndroidEGLGraphicsContext::InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, - int desiredBackbufferSizeY, - 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(); diff --git a/android/jni/AndroidVulkanContext.cpp b/android/jni/AndroidVulkanContext.cpp index 29ad3f3103..18463411c2 100644 --- a/android/jni/AndroidVulkanContext.cpp +++ b/android/jni/AndroidVulkanContext.cpp @@ -74,9 +74,7 @@ AndroidVulkanContext::~AndroidVulkanContext() { g_Vulkan = nullptr; } -bool AndroidVulkanContext::InitFromRenderThread(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(); From bd2c875c9a7d73a930acac3bf9bc04568990ab45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 28 Jan 2018 21:28:16 +0100 Subject: [PATCH 94/96] Fix readback color conversion --- ext/native/thin3d/GLQueueRunner.cpp | 48 +++++++++++++++++++---------- ext/native/thin3d/GLQueueRunner.h | 3 ++ 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 3ea3636968..333ebd6d74 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -41,7 +41,7 @@ void GLQueueRunner::CreateDeviceObjects() { populate(GL_RENDERER); populate(GL_VERSION); populate(GL_SHADING_LANGUAGE_VERSION); - populate(GL_EXTENSIONS); + populate(GL_EXTENSIONS); // TODO: Not OK to query this in core profile! } void GLQueueRunner::DestroyDeviceObjects() { @@ -52,6 +52,10 @@ void GLQueueRunner::DestroyDeviceObjects() { if (gl_extensions.ARB_vertex_array_object) { glDeleteVertexArrays(1, &globalVAO_); } + delete[] readbackBuffer_; + readbackBufferSize_ = 0; + delete[] tempBuffer_; + tempBufferSize_ = 0; } void GLQueueRunner::RunInitSteps(const std::vector &steps) { @@ -969,6 +973,8 @@ void GLQueueRunner::PerformCopy(const GLRStep &step) { } void GLQueueRunner::PerformReadback(const GLRStep &pass) { + using namespace Draw; + GLRFramebuffer *fb = pass.readback.src; fbo_bind_fb_target(true, fb ? fb->handle : 0); @@ -979,17 +985,16 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) { CHECK_GL_ERROR_IF_DEBUG(); - GLuint internalFormat; - GLuint format; - GLuint type; - int alignment; - if (!Draw::Thin3DFormatToFormatAndType(pass.readback.dstFormat, internalFormat, format, type, alignment)) { - ELOG("Readback failed - format %d not available", (int)pass.readback.dstFormat); - return; - } + // Always read back in 8888 format. + const GLuint internalFormat = GL_RGBA; + const GLuint format = GL_RGBA; + const GLuint type = GL_UNSIGNED_BYTE; + const int srcAlignment = 4; + int dstAlignment = DataFormatSizeInBytes(pass.readback.dstFormat); + int pixelStride = pass.readback.srcRect.w; // Apply the correct alignment. - glPixelStorei(GL_PACK_ALIGNMENT, alignment); + glPixelStorei(GL_PACK_ALIGNMENT, srcAlignment); if (!gl_extensions.IsGLES || gl_extensions.GLES3) { // Some drivers seem to require we specify this. See #8254. glPixelStorei(GL_PACK_ROW_LENGTH, pixelStride); @@ -997,21 +1002,32 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) { GLRect2D rect = pass.readback.srcRect; - int size = alignment * rect.w * rect.h; - if (size > readbackBufferSize_) { + bool convert = pass.readback.dstFormat != DataFormat::R8G8B8A8_UNORM; + + int tempSize = srcAlignment * rect.w * rect.h; + int readbackSize = dstAlignment * rect.w * rect.h; + if (convert && tempSize > tempBufferSize_) { + delete[] tempBuffer_; + tempBuffer_ = new uint8_t[tempSize]; + tempBufferSize_ = tempSize; + } + if (readbackSize > readbackBufferSize_) { delete[] readbackBuffer_; - readbackBuffer_ = new uint8_t[size]; - readbackBufferSize_ = size; + readbackBuffer_ = new uint8_t[readbackSize]; + readbackBufferSize_ = readbackSize; } - glReadPixels(rect.x, rect.y, rect.w, rect.h, format, type, readbackBuffer_); - + glReadPixels(rect.x, rect.y, rect.w, rect.h, format, type, convert ? tempBuffer_ : readbackBuffer_); #ifdef DEBUG_READ_PIXELS LogReadPixelsError(glGetError()); #endif if (!gl_extensions.IsGLES || gl_extensions.GLES3) { glPixelStorei(GL_PACK_ROW_LENGTH, 0); } + if (convert) { + ConvertFromRGBA8888(readbackBuffer_, tempBuffer_, pixelStride, pixelStride, rect.w, rect.h, pass.readback.dstFormat); + } + CHECK_GL_ERROR_IF_DEBUG(); } diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index 511e3425a1..c5ff40c832 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -375,6 +375,9 @@ private: // 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; From 3a988400a702ede4137ee3f6d885796c03d727ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 30 Jan 2018 13:32:10 +0100 Subject: [PATCH 95/96] Simpler way to deal with the GL deleter --- ext/native/thin3d/GLQueueRunner.cpp | 47 +++++++++++++++++++++++++-- ext/native/thin3d/GLQueueRunner.h | 2 -- ext/native/thin3d/GLRenderManager.cpp | 13 +++----- ext/native/thin3d/GLRenderManager.h | 21 ++++++------ 4 files changed, 60 insertions(+), 23 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 333ebd6d74..9fd394c8be 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -424,9 +424,52 @@ void GLQueueRunner::RunSteps(const std::vector &steps) { } void GLQueueRunner::LogSteps(const std::vector &steps) { - + for (int i = 0; i < steps.size(); i++) { + const GLRStep &step = *steps[i]; + switch (step.stepType) { + case GLRStepType::RENDER: + LogRenderPass(step); + break; + case GLRStepType::COPY: + LogCopy(step); + break; + case GLRStepType::BLIT: + LogBlit(step); + break; + case GLRStepType::READBACK: + LogReadback(step); + break; + case GLRStepType::READBACK_IMAGE: + LogReadbackImage(step); + break; + default: + Crash(); + break; + } + delete steps[i]; + } } +// TODO: Improve these. +void GLQueueRunner::LogRenderPass(const GLRStep &pass) { + ILOG("RenderPass (%d commands)", (int)pass.commands.size()); +} + +void GLQueueRunner::LogCopy(const GLRStep &pass) { + ILOG("Copy"); +} + +void GLQueueRunner::LogBlit(const GLRStep &pass) { + ILOG("Blit"); +} + +void GLQueueRunner::LogReadback(const GLRStep &pass) { + ILOG("Readback"); +} + +void GLQueueRunner::LogReadbackImage(const GLRStep &pass) { + ILOG("ReadbackImage"); +} void GLQueueRunner::PerformBlit(const GLRStep &step) { // Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing @@ -990,7 +1033,7 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) { const GLuint format = GL_RGBA; const GLuint type = GL_UNSIGNED_BYTE; const int srcAlignment = 4; - int dstAlignment = DataFormatSizeInBytes(pass.readback.dstFormat); + int dstAlignment = (int)DataFormatSizeInBytes(pass.readback.dstFormat); int pixelStride = pass.readback.srcRect.w; // Apply the correct alignment. diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index c5ff40c832..8d50e0ca44 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -355,8 +355,6 @@ private: void LogReadback(const GLRStep &pass); void LogReadbackImage(const GLRStep &pass); - void ResizeReadbackBuffer(size_t requiredSize); - 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); diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 2792e76d1a..abd267aee1 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -13,7 +13,6 @@ #endif void GLDeleter::Perform() { - deleterMutex_.lock(); for (auto shader : shaders) { delete shader; } @@ -38,7 +37,6 @@ void GLDeleter::Perform() { delete framebuffer; } framebuffers.clear(); - deleterMutex_.unlock(); } GLRenderManager::GLRenderManager() { @@ -357,8 +355,6 @@ void GLRenderManager::Finish() { frameData.pull_condVar.notify_all(); } - frameData_[curFrame_].deleter.Take(deleter_); - curFrame_++; if (curFrame_ >= MAX_INFLIGHT_FRAMES) curFrame_ = 0; @@ -380,10 +376,6 @@ void GLRenderManager::Submit(int frame, bool triggerFence) { // When !triggerFence, we notify after syncing with Vulkan. - // Putting deletes here is safe but only because OpenGL has its own delete handling.. - // ideally we'd like to wait a frame or two. - frameData.deleter.Perform(); - if (useThread_ && triggerFence) { VLOG("PULL: Frame %d.readyForFence = true", frame); @@ -414,6 +406,10 @@ 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); @@ -424,6 +420,7 @@ void GLRenderManager::Run(int frame) { switch (frameData.type) { case GLRRunType::END: + frameData.deleter_prev.Take(frameData.deleter); EndSubmitFrame(frame); break; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 6bd2d7caf1..239ff0f3c0 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -148,12 +148,12 @@ enum class GLRRunType { SYNC, }; +// Synchronize this externally if needed, no mutex anymore. class GLDeleter { public: void Perform(); void Take(GLDeleter &other) { - deleterMutex_.lock(); _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); @@ -167,7 +167,6 @@ public: other.textures.clear(); other.inputLayouts.clear(); other.framebuffers.clear(); - deleterMutex_.unlock(); } std::vector shaders; @@ -176,7 +175,6 @@ public: std::vector textures; std::vector inputLayouts; std::vector framebuffers; - std::mutex deleterMutex_; }; class GLRInputLayout { @@ -291,22 +289,22 @@ public: } void DeleteShader(GLRShader *shader) { - deleter_.shaders.push_back(shader); + frameData_[curFrame_].deleter.shaders.push_back(shader); } void DeleteProgram(GLRProgram *program) { - deleter_.programs.push_back(program); + frameData_[curFrame_].deleter.programs.push_back(program); } void DeleteBuffer(GLRBuffer *buffer) { - deleter_.buffers.push_back(buffer); + frameData_[curFrame_].deleter.buffers.push_back(buffer); } void DeleteTexture(GLRTexture *texture) { - deleter_.textures.push_back(texture); + frameData_[curFrame_].deleter.textures.push_back(texture); } void DeleteInputLayout(GLRInputLayout *inputLayout) { - deleter_.inputLayouts.push_back(inputLayout); + frameData_[curFrame_].deleter.inputLayouts.push_back(inputLayout); } void DeleteFramebuffer(GLRFramebuffer *framebuffer) { - deleter_.framebuffers.push_back(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); @@ -696,7 +694,10 @@ private: bool hasBegun = false; uint32_t curSwapchainImage = -1; + std::mutex deleter_mutex; GLDeleter deleter; + GLDeleter deleter_prev; + std::set activePushBuffers; }; @@ -721,8 +722,6 @@ private: bool nextFrame = false; bool firstFrame = true; - GLDeleter deleter_; - bool useThread_ = true; int curFrame_ = 0; From 98cfaef6ec2a62e6b0cb97b67a0d51b99dd67e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 31 Jan 2018 12:05:18 +0100 Subject: [PATCH 96/96] Rough fix for threaded GL for Qt. --- Qt/QtMain.cpp | 97 ++++++++++++++++++++++++++++++++++++++++----------- Qt/QtMain.h | 43 +++++++++++++++++++++-- 2 files changed, 117 insertions(+), 23 deletions(-) diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 3030f64354..ea364b08a5 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -29,6 +29,7 @@ #include "QtMain.h" #include "gfx_es2/gpu_features.h" #include "math/math_util.h" +#include "thread/threadutil.h" #include @@ -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(); diff --git a/Qt/QtMain.h b/Qt/QtMain.h index f4f1b3c5c2..8f0538f7c6 100644 --- a/Qt/QtMain.h +++ b/Qt/QtMain.h @@ -19,6 +19,8 @@ QTM_USE_NAMESPACE #endif #include +#include +#include #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 emuThreadState; + + bool useThread_ = false; }; extern MainUI* emugl;