mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-22 12:26:21 +02:00
DrawEngineGLES already knows that every index it generates is below the decoded/transformed vertex count, but only passed the count to glDrawElements. Drivers that need the vertex range before vertex shading, like Mesa's Panfrost, then scan the index data on the CPU for every draw, and their min/max caches can't help since the index push buffer is rewritten every frame. Only used for single-instance draws on desktop GL or GLES3, and only when the caller provides the range, so other DrawIndexed callers are unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
437 lines
9.2 KiB
C++
437 lines
9.2 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <vector>
|
|
#include <set>
|
|
#include <unordered_map>
|
|
|
|
#include "Common/GPU/OpenGL/GLCommon.h"
|
|
#include "Common/GPU/OpenGL/GLFrameData.h"
|
|
#include "Common/GPU/OpenGL/GLProfiler.h"
|
|
#include "Common/GPU/DataFormat.h"
|
|
#include "Common/GPU/Shader.h"
|
|
#include "Common/GPU/thin3d.h"
|
|
#include "Common/Data/Collections/TinySet.h"
|
|
#include "Common/Data/Collections/FastVec.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,
|
|
STENCIL,
|
|
BLEND,
|
|
BLENDCOLOR,
|
|
LOGICOP,
|
|
UNIFORM4I,
|
|
UNIFORM4UI,
|
|
UNIFORM4F,
|
|
UNIFORMMATRIX,
|
|
UNIFORMSTEREOMATRIX,
|
|
TEXTURESAMPLER,
|
|
TEXTURELOD,
|
|
VIEWPORT,
|
|
SCISSOR,
|
|
RASTER,
|
|
CLEAR,
|
|
INVALIDATE,
|
|
BINDPROGRAM,
|
|
BINDTEXTURE,
|
|
BIND_FB_TEXTURE,
|
|
BIND_VERTEX_BUFFER,
|
|
GENMIPS,
|
|
DRAW,
|
|
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 {
|
|
GLRRenderData(GLRRenderCommand _cmd) : cmd(_cmd) {}
|
|
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 {
|
|
GLenum func;
|
|
GLenum sFail;
|
|
GLenum zFail;
|
|
GLenum pass;
|
|
GLboolean enabled;
|
|
uint8_t ref;
|
|
uint8_t compareMask;
|
|
uint8_t writeMask;
|
|
} stencil;
|
|
struct {
|
|
GLRInputLayout *inputLayout;
|
|
GLRBuffer *vertexBuffer;
|
|
GLRBuffer *indexBuffer;
|
|
uint32_t vertexOffset;
|
|
uint32_t indexOffset;
|
|
GLenum mode; // primitive
|
|
GLint first;
|
|
GLint count;
|
|
GLint indexType;
|
|
GLint instances;
|
|
GLint maxIndex; // -1 if unknown. Otherwise, all indices are known to be <= maxIndex.
|
|
} draw;
|
|
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 {
|
|
GLRFramebuffer* framebuffer;
|
|
int slot;
|
|
int aspect;
|
|
} bind_fb_texture;
|
|
struct {
|
|
GLRBuffer *buffer;
|
|
GLuint target;
|
|
} bind_buffer;
|
|
struct {
|
|
GLRProgram *program;
|
|
} program;
|
|
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 {
|
|
GLenum frontFace;
|
|
GLenum cullFace;
|
|
GLboolean cullEnable;
|
|
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_SUBIMAGE,
|
|
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;
|
|
Draw::DataFormat format;
|
|
int level;
|
|
uint16_t x;
|
|
uint16_t y;
|
|
uint16_t width;
|
|
uint16_t height;
|
|
GLRAllocType allocType;
|
|
uint8_t *data; // owned, delete[]-d
|
|
} texture_subimage;
|
|
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 GLRAspect {
|
|
GLR_ASPECT_COLOR = 1,
|
|
GLR_ASPECT_DEPTH = 2,
|
|
GLR_ASPECT_STENCIL = 3,
|
|
};
|
|
const char *GLRAspectToString(GLRAspect aspect);
|
|
|
|
struct GLRStep {
|
|
GLRStep(GLRStepType _type) : stepType(_type), tag() {}
|
|
GLRStepType stepType;
|
|
FastVec<GLRRenderData> commands;
|
|
TinySet<const GLRFramebuffer *, 8> dependencies;
|
|
const char *tag;
|
|
union {
|
|
struct {
|
|
GLRFramebuffer *framebuffer;
|
|
GLRRenderPassAction color;
|
|
GLRRenderPassAction depth;
|
|
GLRRenderPassAction stencil;
|
|
} 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 {
|
|
GLRFramebuffer* src;
|
|
GLRect2D srcRect;
|
|
int aspectMask;
|
|
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 FastVec<GLRInitStep> &steps, bool skipGLCalls);
|
|
|
|
void RunSteps(const std::vector<GLRStep *> &steps, GLFrameData &frameData, 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;
|
|
}
|
|
|
|
std::string GetGLString(int name) const;
|
|
|
|
private:
|
|
void InitCreateFramebuffer(const GLRInitStep &step);
|
|
|
|
void PerformBindFramebufferAsRenderTarget(const GLRStep &pass);
|
|
void PerformRenderPass(const GLRStep &pass, bool first, bool last, GLQueueProfileContext &profile);
|
|
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); // Unused on some platforms
|
|
void fbo_bind_fb_target(bool read, GLuint name);
|
|
GLenum fbo_get_fb_target(bool read, GLuint **cached);
|
|
void fbo_unbind();
|
|
|
|
std::string StepToString(const GLRStep &step) const;
|
|
|
|
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<int, std::string> glStrings_;
|
|
|
|
bool sawOutOfMemory_ = false;
|
|
bool useDebugGroups_ = false;
|
|
|
|
GLProfiler profiler_;
|
|
|
|
ErrorCallbackFn errorCallback_ = nullptr;
|
|
void *errorCallbackUserData_ = nullptr;
|
|
};
|
|
|
|
const char *RenderCommandToString(GLRRenderCommand cmd);
|