mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Turns out the VR work bloated it a bit, which can't be good. Think it's fine to allocate these view matrices on the heap to get them out of the way, there won't be that crazy many per frame usually.
434 lines
8.8 KiB
C++
434 lines
8.8 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/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<GLRRenderData> commands;
|
|
TinySet<const GLRFramebuffer *, 8> 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<GLRInitStep> &steps, bool skipGLCalls);
|
|
|
|
void RunSteps(const std::vector<GLRStep *> &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<int, std::string> glStrings_;
|
|
|
|
bool sawOutOfMemory_ = false;
|
|
bool useDebugGroups_ = false;
|
|
|
|
ErrorCallbackFn errorCallback_ = nullptr;
|
|
void *errorCallbackUserData_ = nullptr;
|
|
};
|