#pragma once #include #include #include #include #include "Common/GPU/OpenGL/GLCommon.h" #include "Common/GPU/OpenGL/GLFrameData.h" #include "Common/GPU/DataFormat.h" #include "Common/GPU/Shader.h" #include "Common/GPU/thin3d.h" #include "Common/Data/Collections/TinySet.h" struct GLRViewport { float x, y, w, h, minZ, maxZ; }; struct GLRect2D { int x, y, w, h; }; struct GLOffset2D { int x, y; }; enum class GLRAllocType : uint8_t { NONE, NEW, ALIGNED, }; class GLRShader; class GLRTexture; class GLRBuffer; class GLRFramebuffer; class GLRProgram; class GLRInputLayout; enum class GLRRenderCommand : uint8_t { DEPTH, STENCILFUNC, STENCILOP, BLEND, BLENDCOLOR, LOGICOP, UNIFORM4I, UNIFORM4UI, UNIFORM4F, UNIFORMMATRIX, UNIFORMSTEREOMATRIX, TEXTURESAMPLER, TEXTURELOD, VIEWPORT, SCISSOR, RASTER, CLEAR, INVALIDATE, BINDPROGRAM, BINDTEXTURE, BIND_FB_TEXTURE, BIND_VERTEX_BUFFER, BIND_BUFFER, GENMIPS, DRAW, DRAW_INDEXED, TEXTURE_SUBIMAGE, }; // TODO: Bloated since the biggest struct decides the size. Will need something more efficient (separate structs with shared // type field, smashed right after each other?) // Also, all GLenums are really only 16 bits. struct GLRRenderData { GLRRenderCommand cmd; union { struct { GLboolean enabled; GLenum srcColor; GLenum dstColor; GLenum srcAlpha; GLenum dstAlpha; GLenum funcColor; GLenum funcAlpha; int mask; } blend; struct { float color[4]; } blendColor; struct { GLboolean enabled; GLenum logicOp; } logic; struct { GLboolean enabled; GLboolean write; GLenum func; } depth; struct { GLboolean enabled; GLenum func; uint8_t ref; uint8_t compareMask; } stencilFunc; struct { GLenum sFail; GLenum zFail; GLenum pass; uint8_t writeMask; } stencilOp; // also write mask struct { GLenum mode; // primitive GLint buffer; GLint first; GLint count; } draw; struct { GLenum mode; // primitive GLint count; GLint instances; GLint indexType; void *indices; } drawIndexed; struct { const char *name; // if null, use loc const 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 const GLint *loc; float m[16]; } uniformMatrix4; struct { const char *name; // if null, use loc const GLint *loc; float *mData; // new'd, 32 entries } uniformStereoMatrix4; struct { uint32_t clearColor; float clearZ; uint8_t clearStencil; uint8_t colorMask; // Like blend, but for the clear. GLuint clearMask; // GL_COLOR_BUFFER_BIT etc int16_t scissorX; int16_t scissorY; int16_t scissorW; int16_t scissorH; } clear; // also used for invalidate struct { int slot; GLRTexture *texture; } texture; struct { GLRTexture *texture; Draw::DataFormat format; uint8_t slot; uint8_t level; uint16_t width; uint16_t height; uint16_t x; uint16_t y; GLRAllocType allocType; uint8_t *data; // owned, delete[]-d } texture_subimage; struct { int slot; GLRFramebuffer *framebuffer; int aspect; } bind_fb_texture; struct { GLRBuffer *buffer; GLuint target; } bind_buffer; struct { GLRProgram *program; } program; struct { GLRInputLayout *inputLayout; GLRBuffer *buffer; size_t offset; } bindVertexBuffer; struct { int slot; GLenum wrapS; GLenum wrapT; GLenum magFilter; GLenum minFilter; // also includes mip. GL... float anisotropy; } textureSampler; struct { int slot; float minLod; float maxLod; float lodBias; } textureLod; struct { GLRViewport vp; } viewport; struct { GLRect2D rc; } scissor; struct { GLboolean cullEnable; GLenum frontFace; GLenum cullFace; GLboolean ditherEnable; GLboolean depthClampEnable; } raster; }; }; // Unlike in Vulkan, we can't create stuff on the main thread, but need to // defer this too. A big benefit will be that we'll be able to do all creation // at the start of the frame. enum class GLRInitStepType : uint8_t { CREATE_TEXTURE, CREATE_SHADER, CREATE_PROGRAM, CREATE_BUFFER, CREATE_INPUT_LAYOUT, CREATE_FRAMEBUFFER, TEXTURE_IMAGE, TEXTURE_FINALIZE, BUFFER_SUBDATA, }; struct GLRInitStep { GLRInitStep(GLRInitStepType _type) : stepType(_type) {} GLRInitStepType stepType; union { struct { GLRTexture *texture; } create_texture; struct { GLRShader *shader; // This char arrays needs to be allocated with new[]. char *code; GLuint stage; } create_shader; struct { GLRProgram *program; GLRShader *shaders[3]; int num_shaders; bool support_dual_source; } create_program; struct { GLRBuffer *buffer; int size; GLuint usage; } create_buffer; struct { GLRInputLayout *inputLayout; } create_input_layout; struct { GLRFramebuffer *framebuffer; } create_framebuffer; struct { GLRBuffer *buffer; int offset; int size; uint8_t *data; // owned, delete[]-d bool deleteData; } buffer_subdata; struct { GLRTexture *texture; Draw::DataFormat format; int level; uint16_t width; uint16_t height; uint16_t depth; GLRAllocType allocType; bool linearFilter; uint8_t *data; // owned, delete[]-d } texture_image; struct { GLRTexture *texture; int loadedLevels; bool genMips; } texture_finalize; }; }; enum class GLRStepType : uint8_t { RENDER, COPY, BLIT, READBACK, READBACK_IMAGE, RENDER_SKIP, }; 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; TinySet dependencies; const char *tag; union { struct { GLRFramebuffer *framebuffer; GLRRenderPassAction color; GLRRenderPassAction depth; GLRRenderPassAction stencil; // Note: not accurate. int numDraws; } render; struct { GLRFramebuffer *src; GLRFramebuffer *dst; GLRect2D srcRect; GLOffset2D dstPos; int aspectMask; } copy; struct { GLRFramebuffer *src; GLRFramebuffer *dst; GLRect2D srcRect; GLRect2D dstRect; int aspectMask; GLboolean filter; } blit; struct { int aspectMask; GLRFramebuffer *src; GLRect2D srcRect; Draw::DataFormat dstFormat; } readback; struct { GLRTexture *texture; GLRect2D srcRect; int mipLevel; } readback_image; }; }; class GLQueueRunner { public: GLQueueRunner() {} void SetErrorCallback(ErrorCallbackFn callback, void *userdata) { errorCallback_ = callback; errorCallbackUserData_ = userdata; } void SetDeviceCaps(const Draw::DeviceCaps &caps) { caps_ = caps; } void RunInitSteps(const std::vector &steps, bool skipGLCalls); void RunSteps(const std::vector &steps, bool skipGLCalls, bool keepSteps, bool useVR); void CreateDeviceObjects(); void DestroyDeviceObjects(); void CopyFromReadbackBuffer(GLRFramebuffer *framebuffer, int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels); void Resize(int width, int height) { targetWidth_ = width; targetHeight_ = height; } bool SawOutOfMemory() { return sawOutOfMemory_; } std::string GetGLString(int name) const { auto it = glStrings_.find(name); return it != glStrings_.end() ? it->second : ""; } private: void InitCreateFramebuffer(const GLRInitStep &step); void PerformBindFramebufferAsRenderTarget(const GLRStep &pass); void PerformRenderPass(const GLRStep &pass, bool first, bool last); void PerformCopy(const GLRStep &pass); void PerformBlit(const GLRStep &pass); void PerformReadback(const GLRStep &pass); void PerformReadbackImage(const GLRStep &pass); void fbo_ext_create(const GLRInitStep &step); void fbo_bind_fb_target(bool read, GLuint name); GLenum fbo_get_fb_target(bool read, GLuint **cached); void fbo_unbind(); GLRFramebuffer *curFB_ = nullptr; GLuint globalVAO_ = 0; Draw::DeviceCaps caps_{}; // For sanity checks. int curFBWidth_ = 0; int curFBHeight_ = 0; int targetWidth_ = 0; int targetHeight_ = 0; // Readback buffer. Currently we only support synchronous readback, so we only really need one. // We size it generously. uint8_t *readbackBuffer_ = nullptr; int readbackBufferSize_ = 0; uint32_t readbackAspectMask_ = 0; float maxAnisotropyLevel_ = 0.0f; // Framebuffer state? GLuint currentDrawHandle_ = 0; GLuint currentReadHandle_ = 0; std::unordered_map glStrings_; bool sawOutOfMemory_ = false; bool useDebugGroups_ = false; ErrorCallbackFn errorCallback_ = nullptr; void *errorCallbackUserData_ = nullptr; };