CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/GPU/OpenGL/thin3d_gl.cpp
Views: 1401
#include <cstdio>1#include <vector>2#include <string>3#include <algorithm>4#include <map>56#include "ppsspp_config.h"78#include "Common/Data/Convert/ColorConv.h"9#include "Common/Data/Convert/SmallDataConvert.h"10#include "Common/Math/math_util.h"11#include "Common/Math/lin/matrix4x4.h"12#include "Common/System/System.h"13#include "Common/Log.h"14#include "Common/GPU/thin3d.h"15#include "Common/GPU/Shader.h"16#include "Common/GPU/OpenGL/DataFormatGL.h"17#include "Common/GPU/OpenGL/GLCommon.h"18#include "Common/GPU/OpenGL/GLDebugLog.h"19#include "Common/GPU/OpenGL/GLFeatures.h"2021#include "Common/GPU/OpenGL/GLRenderManager.h"2223// #define DEBUG_READ_PIXELS 12425namespace Draw {2627static const unsigned short compToGL[] = {28GL_NEVER,29GL_LESS,30GL_EQUAL,31GL_LEQUAL,32GL_GREATER,33GL_NOTEQUAL,34GL_GEQUAL,35GL_ALWAYS36};3738static const unsigned short blendEqToGL[] = {39GL_FUNC_ADD,40GL_FUNC_SUBTRACT,41GL_FUNC_REVERSE_SUBTRACT,42GL_MIN,43GL_MAX,44};4546static const unsigned short blendFactorToGL[] = {47GL_ZERO,48GL_ONE,49GL_SRC_COLOR,50GL_ONE_MINUS_SRC_COLOR,51GL_DST_COLOR,52GL_ONE_MINUS_DST_COLOR,53GL_SRC_ALPHA,54GL_ONE_MINUS_SRC_ALPHA,55GL_DST_ALPHA,56GL_ONE_MINUS_DST_ALPHA,57GL_CONSTANT_COLOR,58GL_ONE_MINUS_CONSTANT_COLOR,59GL_CONSTANT_ALPHA,60GL_ONE_MINUS_CONSTANT_ALPHA,61#if !defined(USING_GLES2) // TODO: Remove when we have better headers62GL_SRC1_COLOR,63GL_ONE_MINUS_SRC1_COLOR,64GL_SRC1_ALPHA,65GL_ONE_MINUS_SRC1_ALPHA,66#elif !PPSSPP_PLATFORM(IOS)67GL_SRC1_COLOR_EXT,68GL_ONE_MINUS_SRC1_COLOR_EXT,69GL_SRC1_ALPHA_EXT,70GL_ONE_MINUS_SRC1_ALPHA_EXT,71#else72GL_INVALID_ENUM,73GL_INVALID_ENUM,74GL_INVALID_ENUM,75GL_INVALID_ENUM,76#endif77};7879static const unsigned short texWrapToGL[] = {80GL_REPEAT,81GL_MIRRORED_REPEAT,82GL_CLAMP_TO_EDGE,83#if !defined(USING_GLES2)84GL_CLAMP_TO_BORDER,85#else86GL_CLAMP_TO_EDGE,87#endif88};8990static const unsigned short texFilterToGL[] = {91GL_NEAREST,92GL_LINEAR,93};9495static const unsigned short texMipFilterToGL[2][2] = {96// Min nearest:97{ GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR },98// Min linear:99{ GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR },100};101102#ifndef USING_GLES2103static const unsigned short logicOpToGL[] = {104GL_CLEAR,105GL_SET,106GL_COPY,107GL_COPY_INVERTED,108GL_NOOP,109GL_INVERT,110GL_AND,111GL_NAND,112GL_OR,113GL_NOR,114GL_XOR,115GL_EQUIV,116GL_AND_REVERSE,117GL_AND_INVERTED,118GL_OR_REVERSE,119GL_OR_INVERTED,120};121#endif122123static const GLuint stencilOpToGL[8] = {124GL_KEEP,125GL_ZERO,126GL_REPLACE,127GL_INCR,128GL_DECR,129GL_INVERT,130GL_INCR_WRAP,131GL_DECR_WRAP,132};133134static const unsigned short primToGL[] = {135GL_POINTS,136GL_LINES,137GL_LINE_STRIP,138GL_TRIANGLES,139GL_TRIANGLE_STRIP,140GL_TRIANGLE_FAN,141};142143class OpenGLBuffer;144145class OpenGLBlendState : public BlendState {146public:147bool enabled;148GLuint eqCol, eqAlpha;149GLuint srcCol, srcAlpha, dstCol, dstAlpha;150int colorMask;151// uint32_t fixedColor;152153void Apply(GLRenderManager *render) {154render->SetBlendAndMask(colorMask, enabled, srcCol, dstCol, srcAlpha, dstAlpha, eqCol, eqAlpha);155}156};157158class OpenGLSamplerState : public SamplerState {159public:160GLint wrapU;161GLint wrapV;162GLint wrapW;163GLint magFilt;164GLint minFilt;165GLint mipMinFilt;166};167168class OpenGLDepthStencilState : public DepthStencilState {169public:170bool depthTestEnabled;171bool depthWriteEnabled;172GLuint depthComp;173// TODO: Two-sided. Although in practice, do we care?174bool stencilEnabled;175GLuint stencilFail;176GLuint stencilZFail;177GLuint stencilPass;178GLuint stencilCompareOp;179180void Apply(GLRenderManager *render, uint8_t stencilRef, uint8_t stencilWriteMask, uint8_t stencilCompareMask) {181render->SetDepth(depthTestEnabled, depthWriteEnabled, depthComp);182render->SetStencil(183stencilEnabled, stencilCompareOp, stencilRef, stencilCompareMask,184stencilWriteMask, stencilFail, stencilZFail, stencilPass);185}186};187188class OpenGLRasterState : public RasterState {189public:190void Apply(GLRenderManager *render) {191render->SetRaster(cullEnable, frontFace, cullMode, GL_FALSE, GL_FALSE);192}193194GLboolean cullEnable;195GLenum cullMode;196GLenum frontFace;197};198199GLuint ShaderStageToOpenGL(ShaderStage stage) {200switch (stage) {201case ShaderStage::Vertex: return GL_VERTEX_SHADER;202#ifndef USING_GLES2203case ShaderStage::Compute: return GL_COMPUTE_SHADER;204case ShaderStage::Geometry: return GL_GEOMETRY_SHADER;205#endif206case ShaderStage::Fragment:207default:208return GL_FRAGMENT_SHADER;209}210}211212class OpenGLShaderModule : public ShaderModule {213public:214OpenGLShaderModule(GLRenderManager *render, ShaderStage stage, const std::string &tag) : render_(render), stage_(stage), tag_(tag) {215glstage_ = ShaderStageToOpenGL(stage);216}217218~OpenGLShaderModule() {219if (shader_)220render_->DeleteShader(shader_);221}222223bool Compile(GLRenderManager *render, ShaderLanguage language, const uint8_t *data, size_t dataSize);224GLRShader *GetShader() const {225return shader_;226}227const std::string &GetSource() const { return source_; }228229ShaderLanguage GetLanguage() {230return language_;231}232ShaderStage GetStage() const override {233return stage_;234}235236private:237GLRenderManager *render_;238ShaderStage stage_;239ShaderLanguage language_ = GLSL_1xx;240GLRShader *shader_ = nullptr;241GLuint glstage_ = 0;242std::string source_; // So we can recompile in case of context loss.243std::string tag_;244};245246bool OpenGLShaderModule::Compile(GLRenderManager *render, ShaderLanguage language, const uint8_t *data, size_t dataSize) {247source_ = std::string((const char *)data);248// Add the prelude on automatically.249if (glstage_ == GL_FRAGMENT_SHADER || glstage_ == GL_VERTEX_SHADER) {250if (source_.find("#version") == source_.npos) {251source_ = ApplyGLSLPrelude(source_, glstage_);252}253} else {254// Unsupported shader type255return false;256}257258shader_ = render->CreateShader(glstage_, source_, tag_);259_assert_(shader_ != nullptr); // normally can't fail since we defer creation, unless there's a memory error or similar.260return true;261}262263struct PipelineLocData : GLRProgramLocData {264GLint samplerLocs_[MAX_TEXTURE_SLOTS]{};265std::vector<GLint> dynamicUniformLocs_;266};267268class OpenGLInputLayout : public InputLayout {269public:270OpenGLInputLayout(GLRenderManager *render) : render_(render) {}271~OpenGLInputLayout();272273void Compile(const InputLayoutDesc &desc);274275GLRInputLayout *inputLayout_ = nullptr;276int stride = 0;277private:278GLRenderManager *render_;279};280281class OpenGLPipeline : public Pipeline {282public:283OpenGLPipeline(GLRenderManager *render) : render_(render) {}284~OpenGLPipeline() {285for (auto &iter : shaders) {286iter->Release();287}288if (program_) {289render_->DeleteProgram(program_);290}291// DO NOT delete locs_ here, it's deleted by the render manager.292}293294bool LinkShaders(const PipelineDesc &desc);295296GLuint prim = 0;297std::vector<OpenGLShaderModule *> shaders;298AutoRef<OpenGLInputLayout> inputLayout;299AutoRef<OpenGLDepthStencilState> depthStencil;300AutoRef<OpenGLBlendState> blend;301AutoRef<OpenGLRasterState> raster;302303// Not owned!304PipelineLocData *locs_ = nullptr;305306// TODO: Optimize by getting the locations first and putting in a custom struct307UniformBufferDesc dynamicUniforms;308GLRProgram *program_ = nullptr;309310311// Allow using other sampler names than sampler0, sampler1 etc in shaders.312// If not set, will default to those, though.313Slice<SamplerDef> samplers_;314315private:316GLRenderManager *render_;317};318319class OpenGLFramebuffer;320class OpenGLTexture;321322class OpenGLContext : public DrawContext {323public:324OpenGLContext(bool canChangeSwapInterval);325~OpenGLContext();326327void SetTargetSize(int w, int h) override {328DrawContext::SetTargetSize(w, h);329renderManager_.Resize(w, h);330}331332const DeviceCaps &GetDeviceCaps() const override {333return caps_;334}335uint32_t GetSupportedShaderLanguages() const override {336if (gl_extensions.GLES3) {337return (uint32_t)(ShaderLanguage::GLSL_3xx | ShaderLanguage::GLSL_1xx);338} else {339return (uint32_t)ShaderLanguage::GLSL_1xx;340}341}342343uint32_t GetDataFormatSupport(DataFormat fmt) const override;344345void SetErrorCallback(ErrorCallbackFn callback, void *userdata) override {346renderManager_.SetErrorCallback(callback, userdata);347}348349DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) override;350BlendState *CreateBlendState(const BlendStateDesc &desc) override;351SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override;352RasterState *CreateRasterState(const RasterStateDesc &desc) override;353Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override;354InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override;355ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override;356357Texture *CreateTexture(const TextureDesc &desc) override;358Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;359Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override;360361void BeginFrame(DebugFlags debugFlags) override;362void EndFrame() override;363void Present(PresentMode mode, int vblanks) override;364365int GetFrameCount() override {366return frameCount_;367}368369void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override;370void UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) override;371372void 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, const char *tag) override;373bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override;374bool CopyFramebufferToMemory(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, ReadbackMode mode, const char *tag) override;375376// These functions should be self explanatory.377void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;378void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) override;379380void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;381382void BindSamplerStates(int start, int count, SamplerState **states) override {383_assert_(start + count <= MAX_TEXTURE_SLOTS);384for (int i = 0; i < count; i++) {385int index = i + start;386boundSamplers_[index] = static_cast<OpenGLSamplerState *>(states[i]);387}388}389390void SetScissorRect(int left, int top, int width, int height) override {391renderManager_.SetScissor({ left, top, width, height });392}393394void SetViewport(const Viewport &viewport) override {395// Same structure, different name.396renderManager_.SetViewport((GLRViewport &)viewport);397}398399void SetBlendFactor(float color[4]) override {400renderManager_.SetBlendFactor(color);401}402403void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override {404stencilRef_ = refValue;405stencilWriteMask_ = writeMask;406stencilCompareMask_ = compareMask;407// Do we need to update on the fly here?408renderManager_.SetStencil(409curPipeline_->depthStencil->stencilEnabled,410curPipeline_->depthStencil->stencilCompareOp,411refValue,412compareMask,413writeMask,414curPipeline_->depthStencil->stencilFail,415curPipeline_->depthStencil->stencilZFail,416curPipeline_->depthStencil->stencilPass);417}418419void BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) override;420void BindNativeTexture(int sampler, void *nativeTexture) override;421422void BindPipeline(Pipeline *pipeline) override;423void BindVertexBuffer(Buffer *buffer, int offset) override {424curVBuffer_ = (OpenGLBuffer *)buffer;425curVBufferOffset_ = offset;426}427void BindIndexBuffer(Buffer *indexBuffer, int offset) override {428curIBuffer_ = (OpenGLBuffer *)indexBuffer;429curIBufferOffset_ = offset;430}431432void UpdateDynamicUniformBuffer(const void *ub, size_t size) override;433434// TODO: Add more sophisticated draws.435void Draw(int vertexCount, int offset) override;436void DrawIndexed(int vertexCount, int offset) override;437void DrawUP(const void *vdata, int vertexCount) override;438439void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override;440441std::string GetInfoString(InfoField info) const override {442// TODO: Make these actually query the right information443switch (info) {444case InfoField::APINAME:445if (gl_extensions.IsGLES) {446return "OpenGL ES";447} else {448return "OpenGL";449}450case InfoField::VENDORSTRING:451return renderManager_.GetGLString(GL_VENDOR);452case InfoField::VENDOR:453switch (caps_.vendor) {454case GPUVendor::VENDOR_AMD: return "VENDOR_AMD";455case GPUVendor::VENDOR_IMGTEC: return "VENDOR_POWERVR";456case GPUVendor::VENDOR_NVIDIA: return "VENDOR_NVIDIA";457case GPUVendor::VENDOR_INTEL: return "VENDOR_INTEL";458case GPUVendor::VENDOR_QUALCOMM: return "VENDOR_ADRENO";459case GPUVendor::VENDOR_ARM: return "VENDOR_ARM";460case GPUVendor::VENDOR_BROADCOM: return "VENDOR_BROADCOM";461case GPUVendor::VENDOR_VIVANTE: return "VENDOR_VIVANTE";462case GPUVendor::VENDOR_APPLE: return "VENDOR_APPLE";463case GPUVendor::VENDOR_MESA: return "VENDOR_MESA";464case GPUVendor::VENDOR_UNKNOWN:465default:466return "VENDOR_UNKNOWN";467}468break;469case InfoField::DRIVER: return renderManager_.GetGLString(GL_RENDERER);470case InfoField::SHADELANGVERSION: return renderManager_.GetGLString(GL_SHADING_LANGUAGE_VERSION);471case InfoField::APIVERSION: return renderManager_.GetGLString(GL_VERSION);472default: return "?";473}474}475476uint64_t GetNativeObject(NativeObject obj, void *srcObject) override;477478void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override {}479480void Invalidate(InvalidationFlags flags) override;481482void SetInvalidationCallback(InvalidationCallback callback) override {483renderManager_.SetInvalidationCallback(callback);484}485486std::string GetGpuProfileString() const override {487return renderManager_.GetGpuProfileString();488}489490private:491void ApplySamplers();492493GLRenderManager renderManager_;494int frameCount_ = 0;495496DeviceCaps caps_{};497498// Bound state499AutoRef<OpenGLSamplerState> boundSamplers_[MAX_TEXTURE_SLOTS];500// Point to GLRTexture directly because they can point to the textures501// in framebuffers too (which also can be bound).502const GLRTexture *boundTextures_[MAX_TEXTURE_SLOTS]{};503504AutoRef<OpenGLPipeline> curPipeline_;505AutoRef<OpenGLBuffer> curVBuffer_;506AutoRef<OpenGLBuffer> curIBuffer_;507int curVBufferOffset_ = 0;508int curIBufferOffset_ = 0;509AutoRef<Framebuffer> curRenderTarget_;510511uint8_t stencilRef_ = 0;512uint8_t stencilWriteMask_ = 0;513uint8_t stencilCompareMask_ = 0;514515// Frames in flight is not such a strict concept as with Vulkan until we start using glBufferStorage and fences.516// But might as well have the structure ready, and can't hurt to rotate buffers.517struct FrameData {518GLPushBuffer *push;519};520FrameData frameData_[GLRenderManager::MAX_INFLIGHT_FRAMES]{};521};522523static constexpr int MakeIntelSimpleVer(int v1, int v2, int v3) {524return (v1 << 16) | (v2 << 8) | v3;525}526527static bool HasIntelDualSrcBug(const int versions[4]) {528// Intel uses a confusing set of at least 3 version numbering schemes. This is the one given to OpenGL.529switch (MakeIntelSimpleVer(versions[0], versions[1], versions[2])) {530case MakeIntelSimpleVer(9, 17, 10):531case MakeIntelSimpleVer(9, 18, 10):532return false;533case MakeIntelSimpleVer(10, 18, 10):534return versions[3] < 4061;535case MakeIntelSimpleVer(10, 18, 14):536return versions[3] < 4080;537default:538// Older than above didn't support dual src anyway, newer should have the fix.539return false;540}541}542543OpenGLContext::OpenGLContext(bool canChangeSwapInterval) : renderManager_(frameTimeHistory_) {544if (gl_extensions.IsGLES) {545if (gl_extensions.OES_packed_depth_stencil || gl_extensions.OES_depth24) {546caps_.preferredDepthBufferFormat = DataFormat::D24_S8;547} else {548caps_.preferredDepthBufferFormat = DataFormat::D16;549}550if (gl_extensions.GLES3) {551// Mali reports 30 but works fine...552if (gl_extensions.range[1][5][1] >= 30) {553caps_.fragmentShaderInt32Supported = true;554}555}556caps_.texture3DSupported = gl_extensions.OES_texture_3D;557caps_.textureDepthSupported = gl_extensions.GLES3 || gl_extensions.OES_depth_texture;558} else {559if (gl_extensions.VersionGEThan(3, 3, 0)) {560caps_.fragmentShaderInt32Supported = true;561}562caps_.preferredDepthBufferFormat = DataFormat::D24_S8;563caps_.texture3DSupported = true;564caps_.textureDepthSupported = true;565}566567caps_.setMaxFrameLatencySupported = true;568caps_.dualSourceBlend = gl_extensions.ARB_blend_func_extended || gl_extensions.EXT_blend_func_extended;569caps_.anisoSupported = gl_extensions.EXT_texture_filter_anisotropic;570caps_.framebufferCopySupported = gl_extensions.OES_copy_image || gl_extensions.NV_copy_image || gl_extensions.EXT_copy_image || gl_extensions.ARB_copy_image;571caps_.framebufferBlitSupported = gl_extensions.NV_framebuffer_blit || gl_extensions.ARB_framebuffer_object || gl_extensions.GLES3;572caps_.framebufferDepthBlitSupported = caps_.framebufferBlitSupported;573caps_.framebufferStencilBlitSupported = caps_.framebufferBlitSupported;574caps_.depthClampSupported = gl_extensions.ARB_depth_clamp || gl_extensions.EXT_depth_clamp;575caps_.blendMinMaxSupported = gl_extensions.EXT_blend_minmax;576caps_.multiSampleLevelsMask = 1; // More could be supported with some work.577578if (gl_extensions.IsGLES) {579caps_.clipDistanceSupported = gl_extensions.EXT_clip_cull_distance || gl_extensions.APPLE_clip_distance;580caps_.cullDistanceSupported = gl_extensions.EXT_clip_cull_distance;581} else {582caps_.clipDistanceSupported = gl_extensions.VersionGEThan(3, 0);583caps_.cullDistanceSupported = gl_extensions.ARB_cull_distance;584}585caps_.textureNPOTFullySupported =586(!gl_extensions.IsGLES && gl_extensions.VersionGEThan(2, 0, 0)) ||587gl_extensions.IsCoreContext || gl_extensions.GLES3 ||588gl_extensions.ARB_texture_non_power_of_two || gl_extensions.OES_texture_npot;589590if (gl_extensions.IsGLES) {591caps_.fragmentShaderDepthWriteSupported = gl_extensions.GLES3;592// There's also GL_EXT_frag_depth but it's rare along with 2.0. Most chips that support it are simply 3.0 chips.593} else {594caps_.fragmentShaderDepthWriteSupported = true;595}596caps_.fragmentShaderStencilWriteSupported = gl_extensions.ARB_shader_stencil_export;597598// GLES has no support for logic framebuffer operations. There doesn't even seem to exist any such extensions.599caps_.logicOpSupported = !gl_extensions.IsGLES;600601// Always the case in GL (which is what we want for PSP flat shade).602caps_.provokingVertexLast = true;603604// Interesting potential hack for emulating GL_DEPTH_CLAMP (use a separate varying, force depth in fragment shader):605// This will induce a performance penalty on many architectures though so a blanket enable of this606// is probably not a good idea.607// https://stackoverflow.com/questions/5960757/how-to-emulate-gl-depth-clamp-nv608609switch (gl_extensions.gpuVendor) {610case GPU_VENDOR_AMD: caps_.vendor = GPUVendor::VENDOR_AMD; break;611case GPU_VENDOR_NVIDIA: caps_.vendor = GPUVendor::VENDOR_NVIDIA; break;612case GPU_VENDOR_ARM: caps_.vendor = GPUVendor::VENDOR_ARM; break;613case GPU_VENDOR_QUALCOMM: caps_.vendor = GPUVendor::VENDOR_QUALCOMM; break;614case GPU_VENDOR_BROADCOM: caps_.vendor = GPUVendor::VENDOR_BROADCOM; break;615case GPU_VENDOR_INTEL: caps_.vendor = GPUVendor::VENDOR_INTEL; break;616case GPU_VENDOR_IMGTEC: caps_.vendor = GPUVendor::VENDOR_IMGTEC; break;617case GPU_VENDOR_VIVANTE: caps_.vendor = GPUVendor::VENDOR_VIVANTE; break;618case GPU_VENDOR_APPLE: caps_.vendor = GPUVendor::VENDOR_APPLE; break;619case GPU_VENDOR_MESA: caps_.vendor = GPUVendor::VENDOR_MESA; break;620case GPU_VENDOR_UNKNOWN:621default:622caps_.vendor = GPUVendor::VENDOR_UNKNOWN;623break;624}625626// Hide D3D9 when we know it likely won't work well.627#if PPSSPP_PLATFORM(WINDOWS)628caps_.supportsD3D9 = true;629if (!strcmp(gl_extensions.model, "Intel(R) Iris(R) Xe Graphics")) {630caps_.supportsD3D9 = false;631}632#endif633634// Very rough heuristic!635caps_.isTilingGPU = gl_extensions.IsGLES && caps_.vendor != GPUVendor::VENDOR_NVIDIA && caps_.vendor != GPUVendor::VENDOR_INTEL;636637for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) {638frameData_[i].push = renderManager_.CreatePushBuffer(i, GL_ARRAY_BUFFER, 64 * 1024, "thin3d_vbuf");639}640641if (!gl_extensions.VersionGEThan(3, 0, 0)) {642// Don't use this extension on sub 3.0 OpenGL versions as it does not seem reliable.643bugs_.Infest(Bugs::DUAL_SOURCE_BLENDING_BROKEN);644} else if (caps_.vendor == GPUVendor::VENDOR_INTEL) {645// Note: this is for Intel drivers with GL3+.646// Also on Intel, see https://github.com/hrydgard/ppsspp/issues/10117647// TODO: Remove entirely sometime reasonably far in driver years after 2015.648const std::string ver = GetInfoString(Draw::InfoField::APIVERSION);649int versions[4]{};650if (sscanf(ver.c_str(), "Build %d.%d.%d.%d", &versions[0], &versions[1], &versions[2], &versions[3]) == 4) {651if (HasIntelDualSrcBug(versions)) {652bugs_.Infest(Bugs::DUAL_SOURCE_BLENDING_BROKEN);653}654}655}656657#if PPSSPP_ARCH(ARMV7)658if (caps_.vendor == GPUVendor::VENDOR_BROADCOM) {659bugs_.Infest(Bugs::RASPBERRY_SHADER_COMP_HANG);660}661#endif662663// Try to detect old Tegra chips by checking for sub 3.0 GL versions. Like Vivante and Broadcom,664// those can't handle NaN values in conditionals.665if (caps_.vendor == GPUVendor::VENDOR_VIVANTE ||666caps_.vendor == GPUVendor::VENDOR_BROADCOM ||667(caps_.vendor == GPUVendor::VENDOR_NVIDIA && !gl_extensions.VersionGEThan(3, 0, 0))) {668bugs_.Infest(Bugs::BROKEN_NAN_IN_CONDITIONAL);669}670671// TODO: Make this check more lenient. Disabled for all right now672// because it murders performance on Mali.673if (caps_.vendor != GPUVendor::VENDOR_NVIDIA) {674bugs_.Infest(Bugs::ANY_MAP_BUFFER_RANGE_SLOW);675}676677if (caps_.vendor == GPUVendor::VENDOR_IMGTEC) {678// See https://github.com/hrydgard/ppsspp/commit/8974cd675e538f4445955e3eac572a9347d84232679// TODO: Should this workaround be removed for newer devices/drivers?680bugs_.Infest(Bugs::PVR_GENMIPMAP_HEIGHT_GREATER);681}682683if (caps_.vendor == GPUVendor::VENDOR_QUALCOMM) {684#if PPSSPP_PLATFORM(ANDROID)685// The bug seems to affect Adreno 3xx and 5xx, and appeared in Android 8.0 Oreo, so API 26.686// See https://github.com/hrydgard/ppsspp/issues/16015#issuecomment-1328316080.687if (gl_extensions.modelNumber < 600 && System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 26)688bugs_.Infest(Bugs::ADRENO_RESOURCE_DEADLOCK);689#endif690}691692#if PPSSPP_PLATFORM(IOS)693// For some reason, this bug does not appear on M1.694if (caps_.vendor == GPUVendor::VENDOR_APPLE) {695bugs_.Infest(Bugs::BROKEN_FLAT_IN_SHADER);696}697#endif698699shaderLanguageDesc_.Init(GLSL_1xx);700701shaderLanguageDesc_.glslVersionNumber = gl_extensions.GLSLVersion();702703snprintf(shaderLanguageDesc_.driverInfo, sizeof(shaderLanguageDesc_.driverInfo),704"%s - GLSL %d", gl_extensions.model, gl_extensions.GLSLVersion());705// Detect shader language features.706if (gl_extensions.IsGLES) {707shaderLanguageDesc_.gles = true;708if (gl_extensions.GLES3) {709shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_3xx;710shaderLanguageDesc_.fragColor0 = "fragColor0";711shaderLanguageDesc_.texture = "texture";712shaderLanguageDesc_.texture3D = "texture";713shaderLanguageDesc_.glslES30 = true;714shaderLanguageDesc_.bitwiseOps = true;715shaderLanguageDesc_.texelFetch = "texelFetch";716shaderLanguageDesc_.varying_vs = "out";717shaderLanguageDesc_.varying_fs = "in";718shaderLanguageDesc_.attribute = "in";719} else {720shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_1xx;721if (gl_extensions.EXT_gpu_shader4) {722shaderLanguageDesc_.bitwiseOps = true;723shaderLanguageDesc_.texelFetch = "texelFetch2D";724}725if (gl_extensions.EXT_blend_func_extended) {726// Oldy moldy GLES, so use the fixed output name.727shaderLanguageDesc_.fragColor1 = "gl_SecondaryFragColorEXT";728}729}730} else {731// I don't know why we were checking for IsCoreContext here before.732if (gl_extensions.VersionGEThan(3, 3, 0)) {733shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_3xx;734shaderLanguageDesc_.fragColor0 = "fragColor0";735shaderLanguageDesc_.texture = "texture";736shaderLanguageDesc_.texture3D = "texture";737shaderLanguageDesc_.glslES30 = true;738shaderLanguageDesc_.bitwiseOps = true;739shaderLanguageDesc_.texelFetch = "texelFetch";740shaderLanguageDesc_.varying_vs = "out";741shaderLanguageDesc_.varying_fs = "in";742shaderLanguageDesc_.attribute = "in";743} else if (gl_extensions.VersionGEThan(3, 0, 0)) {744shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_1xx;745shaderLanguageDesc_.fragColor0 = "fragColor0";746shaderLanguageDesc_.texture = "texture";747shaderLanguageDesc_.texture3D = "texture";748shaderLanguageDesc_.bitwiseOps = true;749shaderLanguageDesc_.texelFetch = "texelFetch";750shaderLanguageDesc_.varying_vs = "out";751shaderLanguageDesc_.varying_fs = "in";752shaderLanguageDesc_.attribute = "in";753} else {754// This too...755shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_1xx;756if (gl_extensions.EXT_gpu_shader4) {757// Older macOS devices seem to have problems defining uint uniforms.758// Let's just assume OpenGL 3.0+ is required.759shaderLanguageDesc_.bitwiseOps = gl_extensions.VersionGEThan(3, 0, 0);760shaderLanguageDesc_.texelFetch = "texelFetch2D";761}762}763}764765// NOTE: We only support framebuffer fetch on ES3 due to past issues..766if (gl_extensions.IsGLES && gl_extensions.GLES3) {767caps_.framebufferFetchSupported = (gl_extensions.EXT_shader_framebuffer_fetch || gl_extensions.ARM_shader_framebuffer_fetch);768769if (gl_extensions.EXT_shader_framebuffer_fetch) {770shaderLanguageDesc_.framebufferFetchExtension = "#extension GL_EXT_shader_framebuffer_fetch : require";771shaderLanguageDesc_.lastFragData = "fragColor0";772} else if (gl_extensions.ARM_shader_framebuffer_fetch) {773shaderLanguageDesc_.framebufferFetchExtension = "#extension GL_ARM_shader_framebuffer_fetch : require";774shaderLanguageDesc_.lastFragData = "gl_LastFragColorARM";775}776}777778if (canChangeSwapInterval) {779caps_.presentInstantModeChange = true;780caps_.presentMaxInterval = 4;781caps_.presentModesSupported = PresentMode::FIFO | PresentMode::IMMEDIATE;782} else {783caps_.presentInstantModeChange = false;784caps_.presentModesSupported = PresentMode::FIFO;785caps_.presentMaxInterval = 1;786}787788renderManager_.SetDeviceCaps(caps_);789}790791OpenGLContext::~OpenGLContext() {792DestroyPresets();793794for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) {795renderManager_.DeletePushBuffer(frameData_[i].push);796}797}798799void OpenGLContext::BeginFrame(DebugFlags debugFlags) {800renderManager_.BeginFrame(debugFlags & DebugFlags::PROFILE_TIMESTAMPS);801FrameData &frameData = frameData_[renderManager_.GetCurFrame()];802frameData.push->Begin();803}804805void OpenGLContext::EndFrame() {806FrameData &frameData = frameData_[renderManager_.GetCurFrame()];807frameData.push->End(); // upload the data!808renderManager_.Finish();809Invalidate(InvalidationFlags::CACHED_RENDER_STATE);810}811812void OpenGLContext::Present(PresentMode presentMode, int vblanks) {813renderManager_.Present();814frameCount_++;815}816817void OpenGLContext::Invalidate(InvalidationFlags flags) {818if (flags & InvalidationFlags::CACHED_RENDER_STATE) {819// Unbind stuff.820for (auto &texture : boundTextures_) {821texture = nullptr;822}823for (auto &sampler : boundSamplers_) {824sampler = nullptr;825}826curPipeline_ = nullptr;827}828}829830InputLayout *OpenGLContext::CreateInputLayout(const InputLayoutDesc &desc) {831OpenGLInputLayout *fmt = new OpenGLInputLayout(&renderManager_);832fmt->Compile(desc);833return fmt;834}835836static GLuint TypeToTarget(TextureType type) {837switch (type) {838#ifndef USING_GLES2839case TextureType::LINEAR1D: return GL_TEXTURE_1D;840#endif841case TextureType::LINEAR2D: return GL_TEXTURE_2D;842case TextureType::LINEAR3D: return GL_TEXTURE_3D;843case TextureType::CUBE: return GL_TEXTURE_CUBE_MAP;844#ifndef USING_GLES2845case TextureType::ARRAY1D: return GL_TEXTURE_1D_ARRAY;846#endif847case TextureType::ARRAY2D: return GL_TEXTURE_2D_ARRAY;848default:849ERROR_LOG(Log::G3D, "Bad texture type %d", (int)type);850return GL_NONE;851}852}853854class OpenGLTexture : public Texture {855public:856OpenGLTexture(GLRenderManager *render, const TextureDesc &desc);857~OpenGLTexture();858859bool HasMips() const {860return mipLevels_ > 1 || generatedMips_;861}862863TextureType GetType() const { return type_; }864void Bind(int stage) {865render_->BindTexture(stage, tex_);866}867int NumMipmaps() const {868return mipLevels_;869}870const GLRTexture *GetTex() const {871return tex_;872}873874void UpdateTextureLevels(GLRenderManager *render, const uint8_t *const *data, int numLevels, TextureCallback initDataCallback);875876private:877void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback initDataCallback);878879GLRenderManager *render_;880GLRTexture *tex_;881882TextureType type_;883int mipLevels_;884bool generateMips_; // Generate mips requested885bool generatedMips_; // Has generated mips886};887888OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : render_(render) {889_dbg_assert_(desc.format != Draw::DataFormat::UNDEFINED);890_dbg_assert_(desc.width > 0 && desc.height > 0 && desc.depth > 0);891_dbg_assert_(desc.type != Draw::TextureType::UNKNOWN);892893generatedMips_ = false;894generateMips_ = desc.generateMips;895width_ = desc.width;896height_ = desc.height;897depth_ = desc.depth;898format_ = desc.format;899type_ = desc.type;900901GLenum target = TypeToTarget(desc.type);902tex_ = render->CreateTexture(target, desc.width, desc.height, 1, desc.mipLevels);903904mipLevels_ = desc.mipLevels;905if (desc.initData.empty())906return;907908UpdateTextureLevels(render, desc.initData.data(), (int)desc.initData.size(), desc.initDataCallback);909}910911void OpenGLTexture::UpdateTextureLevels(GLRenderManager *render, const uint8_t * const *data, int numLevels, TextureCallback initDataCallback) {912int level = 0;913int width = width_;914int height = height_;915int depth = depth_;916for (int i = 0; i < numLevels; i++) {917SetImageData(0, 0, 0, width, height, depth, level, 0, data[i], initDataCallback);918width = (width + 1) / 2;919height = (height + 1) / 2;920depth = (depth + 1) / 2;921level++;922}923mipLevels_ = generateMips_ ? mipLevels_ : level;924925bool genMips = false;926if (numLevels < mipLevels_ && generateMips_) {927// Assumes the texture is bound for editing928genMips = true;929generatedMips_ = true;930}931render->FinalizeTexture(tex_, mipLevels_, genMips);932}933934OpenGLTexture::~OpenGLTexture() {935if (tex_) {936render_->DeleteTexture(tex_);937tex_ = nullptr;938generatedMips_ = false;939}940}941942class OpenGLFramebuffer : public Framebuffer {943public:944OpenGLFramebuffer(GLRenderManager *render, GLRFramebuffer *framebuffer) : render_(render), framebuffer_(framebuffer) {945width_ = framebuffer->width;946height_ = framebuffer->height;947}948~OpenGLFramebuffer() {949render_->DeleteFramebuffer(framebuffer_);950}951952GLRenderManager *render_;953GLRFramebuffer *framebuffer_ = nullptr;954};955956void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback initDataCallback) {957if ((width != width_ || height != height_ || depth != depth_) && level == 0) {958// When switching to texStorage we need to handle this correctly.959width_ = width;960height_ = height;961depth_ = depth;962}963964if (stride == 0)965stride = width;966967size_t alignment = DataFormatSizeInBytes(format_);968// Make a copy of data with stride eliminated.969uint8_t *texData = new uint8_t[(size_t)(width * height * depth * alignment)];970971bool texDataPopulated = false;972if (initDataCallback) {973texDataPopulated = initDataCallback(texData, data, width, height, depth, width * (int)alignment, height * width * (int)alignment);974}975if (texDataPopulated) {976if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) {977format_ = DataFormat::R5G5B5A1_UNORM_PACK16;978ConvertBGRA5551ToABGR1555((u16 *)texData, (const u16 *)texData, width * height * depth);979}980} else {981// Emulate support for DataFormat::A1R5G5B5_UNORM_PACK16.982if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) {983format_ = DataFormat::R5G5B5A1_UNORM_PACK16;984for (int y = 0; y < height; y++) {985ConvertBGRA5551ToABGR1555((u16 *)(texData + y * width * alignment), (const u16 *)(data + y * stride * alignment), width);986}987} else {988for (int y = 0; y < height; y++) {989memcpy(texData + y * width * alignment, data + y * stride * alignment, width * alignment);990}991}992}993994render_->TextureImage(tex_, level, width, height, depth, format_, texData);995}996997#ifdef DEBUG_READ_PIXELS998// TODO: Make more generic.999static void LogReadPixelsError(GLenum error) {1000switch (error) {1001case GL_NO_ERROR:1002break;1003case GL_INVALID_ENUM:1004ERROR_LOG(Log::G3D, "glReadPixels: GL_INVALID_ENUM");1005break;1006case GL_INVALID_VALUE:1007ERROR_LOG(Log::G3D, "glReadPixels: GL_INVALID_VALUE");1008break;1009case GL_INVALID_OPERATION:1010ERROR_LOG(Log::G3D, "glReadPixels: GL_INVALID_OPERATION");1011break;1012case GL_INVALID_FRAMEBUFFER_OPERATION:1013ERROR_LOG(Log::G3D, "glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION");1014break;1015case GL_OUT_OF_MEMORY:1016ERROR_LOG(Log::G3D, "glReadPixels: GL_OUT_OF_MEMORY");1017break;1018#ifndef USING_GLES21019case GL_STACK_UNDERFLOW:1020ERROR_LOG(Log::G3D, "glReadPixels: GL_STACK_UNDERFLOW");1021break;1022case GL_STACK_OVERFLOW:1023ERROR_LOG(Log::G3D, "glReadPixels: GL_STACK_OVERFLOW");1024break;1025#endif1026default:1027ERROR_LOG(Log::G3D, "glReadPixels: %08x", error);1028break;1029}1030}1031#endif10321033bool OpenGLContext::CopyFramebufferToMemory(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels, int pixelStride, ReadbackMode mode, const char *tag) {1034if (gl_extensions.IsGLES && (channelBits & FB_COLOR_BIT) == 0) {1035// Can't readback depth or stencil on GLES.1036return false;1037}1038OpenGLFramebuffer *fb = (OpenGLFramebuffer *)src;1039GLuint aspect = 0;1040if (channelBits & FB_COLOR_BIT)1041aspect |= GL_COLOR_BUFFER_BIT;1042if (channelBits & FB_DEPTH_BIT)1043aspect |= GL_DEPTH_BUFFER_BIT;1044if (channelBits & FB_STENCIL_BIT)1045aspect |= GL_STENCIL_BUFFER_BIT;1046return renderManager_.CopyFramebufferToMemory(fb ? fb->framebuffer_ : nullptr, aspect, x, y, w, h, dataFormat, (uint8_t *)pixels, pixelStride, mode, tag);1047}104810491050Texture *OpenGLContext::CreateTexture(const TextureDesc &desc) {1051return new OpenGLTexture(&renderManager_, desc);1052}10531054void OpenGLContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) {1055OpenGLTexture *tex = (OpenGLTexture *)texture;1056tex->UpdateTextureLevels(&renderManager_, data, numLevels, initDataCallback);1057}10581059DepthStencilState *OpenGLContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) {1060OpenGLDepthStencilState *ds = new OpenGLDepthStencilState();1061ds->depthTestEnabled = desc.depthTestEnabled;1062ds->depthWriteEnabled = desc.depthWriteEnabled;1063ds->depthComp = compToGL[(int)desc.depthCompare];1064ds->stencilEnabled = desc.stencilEnabled;1065ds->stencilCompareOp = compToGL[(int)desc.stencil.compareOp];1066ds->stencilPass = stencilOpToGL[(int)desc.stencil.passOp];1067ds->stencilFail = stencilOpToGL[(int)desc.stencil.failOp];1068ds->stencilZFail = stencilOpToGL[(int)desc.stencil.depthFailOp];1069return ds;1070}10711072BlendState *OpenGLContext::CreateBlendState(const BlendStateDesc &desc) {1073OpenGLBlendState *bs = new OpenGLBlendState();1074bs->enabled = desc.enabled;1075bs->eqCol = blendEqToGL[(int)desc.eqCol];1076bs->srcCol = blendFactorToGL[(int)desc.srcCol];1077bs->dstCol = blendFactorToGL[(int)desc.dstCol];1078bs->eqAlpha = blendEqToGL[(int)desc.eqAlpha];1079bs->srcAlpha = blendFactorToGL[(int)desc.srcAlpha];1080bs->dstAlpha = blendFactorToGL[(int)desc.dstAlpha];1081bs->colorMask = desc.colorMask;1082return bs;1083}10841085SamplerState *OpenGLContext::CreateSamplerState(const SamplerStateDesc &desc) {1086OpenGLSamplerState *samps = new OpenGLSamplerState();1087samps->wrapU = texWrapToGL[(int)desc.wrapU];1088samps->wrapV = texWrapToGL[(int)desc.wrapV];1089samps->wrapW = texWrapToGL[(int)desc.wrapW];1090samps->magFilt = texFilterToGL[(int)desc.magFilter];1091samps->minFilt = texFilterToGL[(int)desc.minFilter];1092samps->mipMinFilt = texMipFilterToGL[(int)desc.minFilter][(int)desc.mipFilter];1093return samps;1094}10951096RasterState *OpenGLContext::CreateRasterState(const RasterStateDesc &desc) {1097OpenGLRasterState *rs = new OpenGLRasterState();1098if (desc.cull == CullMode::NONE) {1099rs->cullEnable = GL_FALSE;1100return rs;1101}1102rs->cullEnable = GL_TRUE;1103switch (desc.frontFace) {1104case Facing::CW:1105rs->frontFace = GL_CW;1106break;1107case Facing::CCW:1108rs->frontFace = GL_CCW;1109break;1110}1111switch (desc.cull) {1112case CullMode::FRONT:1113rs->cullMode = GL_FRONT;1114break;1115case CullMode::BACK:1116rs->cullMode = GL_BACK;1117break;1118case CullMode::FRONT_AND_BACK:1119rs->cullMode = GL_FRONT_AND_BACK;1120break;1121case CullMode::NONE:1122// Unsupported1123break;1124}1125return rs;1126}11271128class OpenGLBuffer : public Buffer {1129public:1130OpenGLBuffer(GLRenderManager *render, size_t size, uint32_t flags) : render_(render) {1131target_ = (flags & BufferUsageFlag::INDEXDATA) ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;1132usage_ = 0;1133if (flags & BufferUsageFlag::DYNAMIC)1134usage_ = GL_STREAM_DRAW;1135else1136usage_ = GL_STATIC_DRAW;1137buffer_ = render->CreateBuffer(target_, size, usage_);1138totalSize_ = size;1139}1140~OpenGLBuffer() {1141render_->DeleteBuffer(buffer_);1142}11431144GLRenderManager *render_;1145GLRBuffer *buffer_;1146GLuint target_;1147GLuint usage_;11481149size_t totalSize_;1150};11511152Buffer *OpenGLContext::CreateBuffer(size_t size, uint32_t usageFlags) {1153return new OpenGLBuffer(&renderManager_, size, usageFlags);1154}11551156void OpenGLContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) {1157OpenGLBuffer *buf = (OpenGLBuffer *)buffer;11581159if (size + offset > buf->totalSize_) {1160Crash();1161}11621163uint8_t *dataCopy = new uint8_t[size];1164memcpy(dataCopy, data, size);1165// if (flags & UPDATE_DISCARD) we could try to orphan the buffer using glBufferData.1166// But we're much better off using separate buffers per FrameData...1167renderManager_.BufferSubdata(buf->buffer_, offset, size, dataCopy);1168}11691170Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) {1171if (!desc.shaders.size()) {1172ERROR_LOG(Log::G3D, "Pipeline requires at least one shader");1173return nullptr;1174}1175if ((uint32_t)desc.prim >= (uint32_t)Primitive::PRIMITIVE_TYPE_COUNT) {1176ERROR_LOG(Log::G3D, "Invalid primitive type");1177return nullptr;1178}1179if (!desc.depthStencil || !desc.blend || !desc.raster) {1180ERROR_LOG(Log::G3D, "Incomplete prim desciption");1181return nullptr;1182}11831184OpenGLPipeline *pipeline = new OpenGLPipeline(&renderManager_);1185for (auto iter : desc.shaders) {1186if (iter) {1187iter->AddRef();1188pipeline->shaders.push_back(static_cast<OpenGLShaderModule *>(iter));1189} else {1190ERROR_LOG(Log::G3D, "ERROR: Tried to create graphics pipeline %s with a null shader module", tag ? tag : "no tag");1191delete pipeline;1192return nullptr;1193}1194}11951196if (desc.uniformDesc) {1197pipeline->dynamicUniforms = *desc.uniformDesc;1198}11991200pipeline->samplers_ = desc.samplers;1201if (pipeline->LinkShaders(desc)) {1202_assert_((u32)desc.prim < ARRAY_SIZE(primToGL));1203// Build the rest of the virtual pipeline object.1204pipeline->prim = primToGL[(int)desc.prim];1205pipeline->depthStencil = (OpenGLDepthStencilState *)desc.depthStencil;1206pipeline->blend = (OpenGLBlendState *)desc.blend;1207pipeline->raster = (OpenGLRasterState *)desc.raster;1208pipeline->inputLayout = (OpenGLInputLayout *)desc.inputLayout;1209return pipeline;1210} else {1211ERROR_LOG(Log::G3D, "Failed to create pipeline %s - shaders failed to link", tag ? tag : "no tag");1212delete pipeline;1213return nullptr;1214}1215}12161217void OpenGLContext::BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) {1218_assert_(start + count <= MAX_TEXTURE_SLOTS);1219for (int i = start; i < start + count; i++) {1220OpenGLTexture *glTex = static_cast<OpenGLTexture *>(textures[i - start]);1221if (!glTex) {1222boundTextures_[i] = nullptr;1223renderManager_.BindTexture(i, nullptr);1224continue;1225}1226glTex->Bind(i);1227boundTextures_[i] = glTex->GetTex();1228}1229}12301231void OpenGLContext::BindNativeTexture(int index, void *nativeTexture) {1232GLRTexture *tex = (GLRTexture *)nativeTexture;1233boundTextures_[index] = tex;1234renderManager_.BindTexture(index, tex);1235}12361237void OpenGLContext::ApplySamplers() {1238for (int i = 0; i < MAX_TEXTURE_SLOTS; i++) {1239const OpenGLSamplerState *samp = boundSamplers_[i];1240const GLRTexture *tex = boundTextures_[i];1241if (tex) {1242_assert_msg_(samp, "Sampler missing");1243} else {1244continue;1245}1246GLenum wrapS;1247GLenum wrapT;1248if (tex->canWrap) {1249wrapS = samp->wrapU;1250wrapT = samp->wrapV;1251} else {1252wrapS = GL_CLAMP_TO_EDGE;1253wrapT = GL_CLAMP_TO_EDGE;1254}1255GLenum magFilt = samp->magFilt;1256GLenum minFilt = tex->numMips > 1 ? samp->mipMinFilt : samp->minFilt;1257renderManager_.SetTextureSampler(i, wrapS, wrapT, magFilt, minFilt, 0.0f);1258renderManager_.SetTextureLod(i, 0.0, (float)(tex->numMips - 1), 0.0);1259}1260}12611262ShaderModule *OpenGLContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) {1263OpenGLShaderModule *shader = new OpenGLShaderModule(&renderManager_, stage, tag);1264if (shader->Compile(&renderManager_, language, data, dataSize)) {1265return shader;1266} else {1267shader->Release();1268return nullptr;1269}1270}12711272bool OpenGLPipeline::LinkShaders(const PipelineDesc &desc) {1273std::vector<GLRShader *> linkShaders;1274for (auto shaderModule : shaders) {1275if (shaderModule) {1276GLRShader *shader = shaderModule->GetShader();1277if (shader) {1278linkShaders.push_back(shader);1279} else {1280ERROR_LOG(Log::G3D, "LinkShaders: Bad shader module");1281return false;1282}1283} else {1284ERROR_LOG(Log::G3D, "LinkShaders: Bad shader in module");1285return false;1286}1287}12881289std::vector<GLRProgram::Semantic> semantics;1290semantics.reserve(8);1291// Bind all the common vertex data points. Mismatching ones will be ignored.1292semantics.push_back({ SEM_POSITION, "Position" });1293semantics.push_back({ SEM_COLOR0, "Color0" });1294semantics.push_back({ SEM_COLOR1, "Color1" });1295semantics.push_back({ SEM_TEXCOORD0, "TexCoord0" });1296semantics.push_back({ SEM_NORMAL, "Normal" });1297semantics.push_back({ SEM_TANGENT, "Tangent" });1298semantics.push_back({ SEM_BINORMAL, "Binormal" });1299// For postshaders.1300semantics.push_back({ SEM_POSITION, "a_position" });1301semantics.push_back({ SEM_TEXCOORD0, "a_texcoord0" });13021303locs_ = new PipelineLocData();1304locs_->dynamicUniformLocs_.resize(desc.uniformDesc->uniforms.size());13051306std::vector<GLRProgram::UniformLocQuery> queries;1307int samplersToCheck;1308if (!samplers_.is_empty()) {1309int size = std::min((const uint32_t)samplers_.size(), MAX_TEXTURE_SLOTS);1310queries.reserve(size);1311for (int i = 0; i < size; i++) {1312queries.push_back({ &locs_->samplerLocs_[i], samplers_[i].name, true });1313}1314samplersToCheck = size;1315} else {1316queries.push_back({ &locs_->samplerLocs_[0], "sampler0" });1317queries.push_back({ &locs_->samplerLocs_[1], "sampler1" });1318queries.push_back({ &locs_->samplerLocs_[2], "sampler2" });1319samplersToCheck = 3;1320}13211322_assert_(queries.size() <= MAX_TEXTURE_SLOTS);1323queries.reserve(dynamicUniforms.uniforms.size());1324for (size_t i = 0; i < dynamicUniforms.uniforms.size(); ++i) {1325queries.push_back({ &locs_->dynamicUniformLocs_[i], dynamicUniforms.uniforms[i].name });1326}1327std::vector<GLRProgram::Initializer> initialize;1328for (int i = 0; i < MAX_TEXTURE_SLOTS; ++i) {1329if (i < samplersToCheck) {1330initialize.push_back({ &locs_->samplerLocs_[i], 0, i });1331} else {1332locs_->samplerLocs_[i] = -1;1333}1334}13351336GLRProgramFlags flags{};1337program_ = render_->CreateProgram(linkShaders, semantics, queries, initialize, locs_, flags);1338return true;1339}13401341void OpenGLContext::BindPipeline(Pipeline *pipeline) {1342curPipeline_ = (OpenGLPipeline *)pipeline;1343if (!curPipeline_) {1344return;1345}1346curPipeline_->blend->Apply(&renderManager_);1347curPipeline_->depthStencil->Apply(&renderManager_, stencilRef_, stencilWriteMask_, stencilCompareMask_);1348curPipeline_->raster->Apply(&renderManager_);1349renderManager_.BindProgram(curPipeline_->program_);1350}13511352void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {1353if (curPipeline_->dynamicUniforms.uniformBufferSize != size) {1354Crash();1355}13561357for (size_t i = 0; i < curPipeline_->dynamicUniforms.uniforms.size(); ++i) {1358const auto &uniform = curPipeline_->dynamicUniforms.uniforms[i];1359const GLint &loc = curPipeline_->locs_->dynamicUniformLocs_[i];1360const float *data = (const float *)((uint8_t *)ub + uniform.offset);1361switch (uniform.type) {1362case UniformType::FLOAT1:1363case UniformType::FLOAT2:1364case UniformType::FLOAT3:1365case UniformType::FLOAT4:1366renderManager_.SetUniformF(&loc, 1 + (int)uniform.type - (int)UniformType::FLOAT1, data);1367break;1368case UniformType::MATRIX4X4:1369renderManager_.SetUniformM4x4(&loc, data);1370break;1371}1372}1373}13741375void OpenGLContext::Draw(int vertexCount, int offset) {1376_dbg_assert_msg_(curVBuffer_ != nullptr, "Can't call Draw without a vertex buffer");1377ApplySamplers();1378_assert_(curPipeline_->inputLayout);1379renderManager_.Draw(curPipeline_->inputLayout->inputLayout_, curVBuffer_->buffer_, curVBufferOffset_, curPipeline_->prim, offset, vertexCount);1380}13811382void OpenGLContext::DrawIndexed(int vertexCount, int offset) {1383_dbg_assert_msg_(curVBuffer_ != nullptr, "Can't call DrawIndexed without a vertex buffer");1384_dbg_assert_msg_(curIBuffer_ != nullptr, "Can't call DrawIndexed without an index buffer");1385ApplySamplers();1386_assert_(curPipeline_->inputLayout);1387renderManager_.DrawIndexed(1388curPipeline_->inputLayout->inputLayout_,1389curVBuffer_->buffer_, curVBufferOffset_,1390curIBuffer_->buffer_, curIBufferOffset_ + offset * sizeof(uint32_t),1391curPipeline_->prim, vertexCount, GL_UNSIGNED_SHORT);1392}13931394void OpenGLContext::DrawUP(const void *vdata, int vertexCount) {1395_assert_(curPipeline_->inputLayout != nullptr);1396int stride = curPipeline_->inputLayout->stride;1397uint32_t dataSize = stride * vertexCount;13981399FrameData &frameData = frameData_[renderManager_.GetCurFrame()];14001401GLRBuffer *buf;1402uint32_t offset;1403uint8_t *dest = frameData.push->Allocate(dataSize, 4, &buf, &offset);1404memcpy(dest, vdata, dataSize);14051406ApplySamplers();1407_assert_(curPipeline_->inputLayout);1408renderManager_.Draw(curPipeline_->inputLayout->inputLayout_, buf, offset, curPipeline_->prim, 0, vertexCount);1409}14101411void OpenGLContext::Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) {1412float col[4];1413Uint8x4ToFloat4(col, colorval);1414GLuint glMask = 0;1415if (mask & FBChannel::FB_COLOR_BIT) {1416glMask |= GL_COLOR_BUFFER_BIT;1417}1418if (mask & FBChannel::FB_DEPTH_BIT) {1419glMask |= GL_DEPTH_BUFFER_BIT;1420}1421if (mask & FBChannel::FB_STENCIL_BIT) {1422glMask |= GL_STENCIL_BUFFER_BIT;1423}1424renderManager_.Clear(colorval, depthVal, stencilVal, glMask, 0xF, 0, 0, targetWidth_, targetHeight_);1425}14261427DrawContext *T3DCreateGLContext(bool canChangeSwapInterval) {1428return new OpenGLContext(canChangeSwapInterval);1429}14301431OpenGLInputLayout::~OpenGLInputLayout() {1432render_->DeleteInputLayout(inputLayout_);1433}14341435void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) {1436// TODO: This is only accurate if there's only one stream. But whatever, for now we1437// never use multiple streams anyway.1438stride = desc.stride;14391440std::vector<GLRInputLayout::Entry> entries;1441for (auto &attr : desc.attributes) {1442GLRInputLayout::Entry entry;1443entry.location = attr.location;1444entry.offset = attr.offset;1445switch (attr.format) {1446case DataFormat::R32G32_FLOAT:1447entry.count = 2;1448entry.type = GL_FLOAT;1449entry.normalized = GL_FALSE;1450break;1451case DataFormat::R32G32B32_FLOAT:1452entry.count = 3;1453entry.type = GL_FLOAT;1454entry.normalized = GL_FALSE;1455break;1456case DataFormat::R32G32B32A32_FLOAT:1457entry.count = 4;1458entry.type = GL_FLOAT;1459entry.normalized = GL_FALSE;1460break;1461case DataFormat::R8G8B8A8_UNORM:1462entry.count = 4;1463entry.type = GL_UNSIGNED_BYTE;1464entry.normalized = GL_TRUE;1465break;1466case DataFormat::UNDEFINED:1467default:1468ERROR_LOG(Log::G3D, "Thin3DGLVertexFormat: Invalid or unknown component type applied.");1469break;1470}14711472entries.push_back(entry);1473}1474if (!entries.empty()) {1475inputLayout_ = render_->CreateInputLayout(entries, stride);1476} else {1477inputLayout_ = nullptr;1478}1479}14801481Framebuffer *OpenGLContext::CreateFramebuffer(const FramebufferDesc &desc) {1482CheckGLExtensions();14831484// TODO: Support multiview later. (It's our only use case for multi layers).1485_dbg_assert_(desc.numLayers == 1);14861487GLRFramebuffer *framebuffer = renderManager_.CreateFramebuffer(desc.width, desc.height, desc.z_stencil, desc.tag);1488OpenGLFramebuffer *fbo = new OpenGLFramebuffer(&renderManager_, framebuffer);1489return fbo;1490}14911492void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) {1493OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;1494GLRRenderPassAction color = (GLRRenderPassAction)rp.color;1495GLRRenderPassAction depth = (GLRRenderPassAction)rp.depth;1496GLRRenderPassAction stencil = (GLRRenderPassAction)rp.stencil;14971498renderManager_.BindFramebufferAsRenderTarget(fb ? fb->framebuffer_ : nullptr, color, depth, stencil, rp.clearColor, rp.clearDepth, rp.clearStencil, tag);1499curRenderTarget_ = fb;1500}15011502void 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, const char *tag) {1503OpenGLFramebuffer *src = (OpenGLFramebuffer *)fbsrc;1504OpenGLFramebuffer *dst = (OpenGLFramebuffer *)fbdst;15051506int aspect = 0;1507if (channelBits & FB_COLOR_BIT) {1508aspect |= GL_COLOR_BUFFER_BIT;1509} else if (channelBits & (FB_STENCIL_BIT | FB_DEPTH_BIT)) {1510if (channelBits & FB_DEPTH_BIT)1511aspect |= GL_DEPTH_BUFFER_BIT;1512if (channelBits & FB_STENCIL_BIT)1513aspect |= GL_STENCIL_BUFFER_BIT;1514}1515renderManager_.CopyFramebuffer(src->framebuffer_, GLRect2D{ srcX, srcY, width, height }, dst->framebuffer_, GLOffset2D{ dstX, dstY }, aspect, tag);1516}15171518bool 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, const char *tag) {1519OpenGLFramebuffer *src = (OpenGLFramebuffer *)fbsrc;1520OpenGLFramebuffer *dst = (OpenGLFramebuffer *)fbdst;1521GLuint aspect = 0;1522if (channels & FB_COLOR_BIT)1523aspect |= GL_COLOR_BUFFER_BIT;1524if (channels & FB_DEPTH_BIT)1525aspect |= GL_DEPTH_BUFFER_BIT;1526if (channels & FB_STENCIL_BIT)1527aspect |= GL_STENCIL_BUFFER_BIT;15281529renderManager_.BlitFramebuffer(src->framebuffer_, GLRect2D{ srcX1, srcY1, srcX2 - srcX1, srcY2 - srcY1 }, dst->framebuffer_, GLRect2D{ dstX1, dstY1, dstX2 - dstX1, dstY2 - dstY1 }, aspect, linearFilter == FB_BLIT_LINEAR, tag);1530return true;1531}15321533void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) {1534OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;1535_assert_(binding < MAX_TEXTURE_SLOTS);15361537GLuint aspect = 0;1538if (channelBit & FB_COLOR_BIT) {1539aspect |= GL_COLOR_BUFFER_BIT;1540boundTextures_[binding] = &fb->framebuffer_->color_texture;1541}1542if (channelBit & FB_DEPTH_BIT) {1543aspect |= GL_DEPTH_BUFFER_BIT;1544boundTextures_[binding] = &fb->framebuffer_->z_stencil_texture;1545}1546if (channelBit & FB_STENCIL_BIT) {1547aspect |= GL_STENCIL_BUFFER_BIT;1548boundTextures_[binding] = &fb->framebuffer_->z_stencil_texture;1549}1550renderManager_.BindFramebufferAsTexture(fb->framebuffer_, binding, aspect);1551}15521553void OpenGLContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) {1554OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;1555if (fb) {1556*w = fb->Width();1557*h = fb->Height();1558} else {1559*w = targetWidth_;1560*h = targetHeight_;1561}1562}15631564uint64_t OpenGLContext::GetNativeObject(NativeObject obj, void *srcObject) {1565switch (obj) {1566case NativeObject::RENDER_MANAGER:1567return (uint64_t)(uintptr_t)&renderManager_;1568case NativeObject::TEXTURE_VIEW: // Gets the GLRTexture *1569return (uint64_t)(((OpenGLTexture *)srcObject)->GetTex());1570default:1571return 0;1572}1573}15741575uint32_t OpenGLContext::GetDataFormatSupport(DataFormat fmt) const {1576switch (fmt) {1577case DataFormat::R4G4B4A4_UNORM_PACK16:1578case DataFormat::R5G6B5_UNORM_PACK16:1579case DataFormat::R5G5B5A1_UNORM_PACK16:1580return FMT_RENDERTARGET | FMT_TEXTURE | FMT_AUTOGEN_MIPS; // native support15811582case DataFormat::R8G8B8A8_UNORM:1583return FMT_RENDERTARGET | FMT_TEXTURE | FMT_INPUTLAYOUT | FMT_AUTOGEN_MIPS;15841585case DataFormat::A1R5G5B5_UNORM_PACK16:1586return FMT_TEXTURE; // we will emulate this! Very fast to convert from R5G5B5A1_UNORM_PACK16 during upload.15871588case DataFormat::R32_FLOAT:1589case DataFormat::R32G32_FLOAT:1590case DataFormat::R32G32B32_FLOAT:1591case DataFormat::R32G32B32A32_FLOAT:1592return FMT_INPUTLAYOUT;15931594case DataFormat::R8_UNORM:1595return FMT_TEXTURE;1596case DataFormat::R16_UNORM:1597if (!gl_extensions.IsGLES) {1598return FMT_TEXTURE;1599} else {1600return 0;1601}1602break;16031604case DataFormat::BC1_RGBA_UNORM_BLOCK:1605case DataFormat::BC2_UNORM_BLOCK:1606case DataFormat::BC3_UNORM_BLOCK:1607return gl_extensions.supportsBC123 ? FMT_TEXTURE : 0;16081609case DataFormat::BC4_UNORM_BLOCK:1610case DataFormat::BC5_UNORM_BLOCK:1611return gl_extensions.supportsBC45 ? FMT_TEXTURE : 0;16121613case DataFormat::BC7_UNORM_BLOCK:1614return gl_extensions.supportsBC7 ? FMT_TEXTURE : 0;16151616case DataFormat::ASTC_4x4_UNORM_BLOCK:1617return gl_extensions.supportsASTC ? FMT_TEXTURE : 0;16181619case DataFormat::ETC2_R8G8B8_UNORM_BLOCK:1620case DataFormat::ETC2_R8G8B8A1_UNORM_BLOCK:1621case DataFormat::ETC2_R8G8B8A8_UNORM_BLOCK:1622return gl_extensions.supportsETC2 ? FMT_TEXTURE : 0;16231624default:1625return 0;1626}1627}16281629} // namespace Draw163016311632