flycast/core/rend/gles/gles.h

586 lines
12 KiB
C++
Executable File

#pragma once
#include "hw/pvr/ta_structs.h"
#include "hw/pvr/ta_ctx.h"
#include "hw/pvr/elan_struct.h"
#include "rend/TexCache.h"
#include "wsi/gl_context.h"
#include "glcache.h"
#include "rend/shader_util.h"
#ifndef LIBRETRO
#include "rend/imgui_driver.h"
#endif
#include <unordered_map>
#include <glm/glm.hpp>
#ifndef GL_TEXTURE_MAX_ANISOTROPY
#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE
#endif
#ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY
#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF
#endif
#ifndef GL_PRIMITIVE_RESTART_FIXED_INDEX
#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69
#endif
#define glCheck() do { if (unlikely(config::OpenGlChecks)) { verify(glGetError()==GL_NO_ERROR); } } while(0)
#define VERTEX_POS_ARRAY 0
#define VERTEX_COL_BASE_ARRAY 1
#define VERTEX_COL_OFFS_ARRAY 2
#define VERTEX_UV_ARRAY 3
// OIT only
#define VERTEX_COL_BASE1_ARRAY 4
#define VERTEX_COL_OFFS1_ARRAY 5
#define VERTEX_UV1_ARRAY 6
// Naomi2
#define VERTEX_NORM_ARRAY 7
//vertex types
extern u32 gcflip;
extern glm::mat4 ViewportMatrix;
void DrawStrips();
struct PipelineShader
{
GLuint program;
GLint depth_scale;
GLint pp_ClipTest;
GLint cp_AlphaTestValue;
GLint sp_FOG_COL_RAM;
GLint sp_FOG_COL_VERT;
GLint sp_FOG_DENSITY;
GLint trilinear_alpha;
GLint fog_clamp_min, fog_clamp_max;
GLint ndcMat;
GLint palette_index;
// Naomi2
GLint mvMat;
GLint normalMat;
GLint projMat;
GLint glossCoef[2];
GLint envMapping[2];
GLint bumpMapping;
GLint constantColor[2];
GLint lightCount;
GLint ambientBase[2];
GLint ambientOffset[2];
GLint ambientMaterialBase[2];
GLint ambientMaterialOffset[2];
GLint useBaseOver;
GLint bumpId0;
GLint bumpId1;
struct {
GLint color;
GLint direction;
GLint position;
GLint parallel;
GLint diffuse[2];
GLint specular[2];
GLint routing;
GLint dmode;
GLint smode;
GLint distAttnMode;
GLint attnDistA;
GLint attnDistB;
GLint attnAngleA;
GLint attnAngleB;
} lights[elan::MAX_LIGHTS];
int lastMvMat;
int lastNormalMat;
int lastProjMat;
int lastLightModel;
//
bool cp_AlphaTest;
bool pp_InsideClipping;
bool pp_Texture;
bool pp_UseAlpha;
bool pp_IgnoreTexA;
u32 pp_ShadInstr;
bool pp_Offset;
u32 pp_FogCtrl;
bool pp_Gouraud;
bool pp_BumpMap;
bool fog_clamping;
bool trilinear;
bool palette;
bool naomi2;
bool divPosZ;
};
class GlBuffer
{
public:
GlBuffer(GLenum type, GLenum usage = GL_STREAM_DRAW)
: type(type), usage(usage), size(0) {
glGenBuffers(1, &name);
}
~GlBuffer() {
glDeleteBuffers(1, &name);
}
void bind() const {
glBindBuffer(type, name);
}
GLuint getName() const {
return name;
}
void update(const void *data, GLsizeiptr size)
{
bind();
if (size > this->size)
{
glBufferData(type, size, data, usage);
this->size = size;
}
else
{
glBufferSubData(type, 0, size, data);
}
}
private:
GLenum type;
GLenum usage;
GLsizeiptr size;
GLuint name;
};
class GlFramebuffer
{
public:
GlFramebuffer(int width, int height, bool withDepth = false, GLuint texture = 0);
GlFramebuffer(int width, int height, bool withDepth, bool withTexture);
~GlFramebuffer();
void bind(GLenum type = GL_FRAMEBUFFER) const {
glBindFramebuffer(type, framebuffer);
}
int getWidth() const { return width; }
int getHeight() const { return height; }
GLuint getTexture() const { return texture; }
GLuint detachTexture() {
GLuint t = texture;
texture = 0;
return t;
}
GLuint getFramebuffer() const { return framebuffer; }
private:
void makeFramebuffer(bool withDepth);
int width;
int height;
GLuint texture;
GLuint framebuffer = 0;
GLuint colorBuffer = 0;
GLuint depthBuffer = 0;
};
class GlVertexArray
{
public:
virtual ~GlVertexArray() = default;
void bind(GlBuffer *buffer, GlBuffer *indexBuffer = nullptr);
static void unbind();
void term();
protected:
virtual void defineVtxAttribs() = 0;
private:
static void bindVertexArray(GLuint vao);
GLuint vertexArray = 0;
};
class MainVertexArray final : public GlVertexArray
{
protected:
void defineVtxAttribs() override;
};
class ModvolVertexArray final : public GlVertexArray
{
protected:
void defineVtxAttribs() override;
};
class OSDVertexArray final : public GlVertexArray
{
protected:
void defineVtxAttribs() override;
};
struct gl_ctx
{
struct
{
GLuint program;
GLint depth_scale;
GLint sp_ShaderColor;
GLint ndcMat;
} modvol_shader;
struct
{
GLuint program;
GLint depth_scale;
GLint sp_ShaderColor;
GLint ndcMat;
GLint mvMat;
GLint projMat;
} n2ModVolShader;
std::unordered_map<u32, PipelineShader> shaders;
struct
{
GLuint program;
GLint scale;
OSDVertexArray vao;
std::unique_ptr<GlBuffer> geometry;
GLuint osd_tex;
} OSD_SHADER;
struct
{
MainVertexArray mainVAO;
ModvolVertexArray modvolVAO;
std::unique_ptr<GlBuffer> geometry;
std::unique_ptr<GlBuffer> modvols;
std::unique_ptr<GlBuffer> idxs;
} vbo;
struct
{
u32 texAddress = ~0;
GLuint pbo;
u32 pboSize;
bool directXfer;
u32 width;
u32 height;
FB_W_CTRL_type fb_w_ctrl;
u32 linestride;
std::unique_ptr<GlFramebuffer> framebuffer;
} rtt;
struct
{
std::unique_ptr<GlFramebuffer> framebuffer;
float aspectRatio;
GLuint origFbo;
float shiftX, shiftY;
} ofbo;
struct
{
GLuint tex;
int width;
int height;
} dcfb;
struct
{
std::unique_ptr<GlFramebuffer> framebuffer;
} fbscaling;
struct
{
std::unique_ptr<GlFramebuffer> framebuffer;
bool ready = false;
} ofbo2;
struct
{
std::unique_ptr<GlFramebuffer> framebuffer;
} videorouting;
const char *gl_version;
const char *glsl_version_header;
int gl_major;
int gl_minor;
bool is_gles;
GLuint single_channel_format;
GLenum index_type;
bool GL_OES_packed_depth_stencil_supported;
bool GL_OES_depth24_supported;
bool highp_float_supported;
float max_anisotropy;
bool mesa_nouveau;
bool border_clamp_supported;
bool prim_restart_supported;
bool prim_restart_fixed_supported;
size_t get_index_size() { return index_type == GL_UNSIGNED_INT ? sizeof(u32) : sizeof(u16); }
};
extern gl_ctx gl;
inline void GlVertexArray::bindVertexArray(GLuint vao)
{
#ifndef GLES2
if (gl.gl_major >= 3)
glBindVertexArray(vao);
#endif
}
inline void GlVertexArray::bind(GlBuffer *buffer, GlBuffer *indexBuffer)
{
if (vertexArray == 0)
{
#ifndef GLES2
if (gl.gl_major >= 3)
{
glGenVertexArrays(1, &vertexArray);
glBindVertexArray(vertexArray);
}
#endif
buffer->bind();
if (indexBuffer != nullptr)
indexBuffer->bind();
else
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
defineVtxAttribs();
}
else
{
bindVertexArray(vertexArray);
buffer->bind();
if (indexBuffer != nullptr)
indexBuffer->bind();
else
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
inline void GlVertexArray::unbind()
{
bindVertexArray(0);
}
inline void GlVertexArray::term()
{
#ifndef GLES2
if (gl.gl_major >= 3)
glDeleteVertexArrays(1, &vertexArray);
#endif
vertexArray = 0;
}
enum ModifierVolumeMode { Xor, Or, Inclusion, Exclusion, ModeCount };
void termGLCommon();
void findGLVersion();
void SetCull(u32 CullMode);
void SetMVS_Mode(ModifierVolumeMode mv_mode, ISP_Modvol ispc);
GLuint BindRTT(bool withDepthBuffer = true);
void ReadRTTBuffer();
void glReadFramebuffer(const FramebufferInfo& info);
GLuint init_output_framebuffer(int width, int height);
void writeFramebufferToVRAM();
PipelineShader *GetProgram(bool cp_AlphaTest, bool pp_InsideClipping,
bool pp_Texture, bool pp_UseAlpha, bool pp_IgnoreTexA, u32 pp_ShadInstr, bool pp_Offset,
u32 pp_FogCtrl, bool pp_Gouraud, bool pp_BumpMap, bool fog_clamping, bool trilinear,
bool palette, bool naomi2);
GLuint gl_CompileShader(const char* shader, GLuint type);
GLuint gl_CompileAndLink(const char *vertexShader, const char *fragmentShader);
bool CompilePipelineShader(PipelineShader* s);
extern const char* GouraudSource;
extern struct ShaderUniforms_t
{
float PT_ALPHA;
float depth_coefs[4];
float fog_den_float;
float ps_FOG_COL_RAM[3];
float ps_FOG_COL_VERT[3];
float trilinear_alpha;
float fog_clamp_min[4];
float fog_clamp_max[4];
glm::mat4 ndcMat;
struct {
bool enabled;
int x;
int y;
int width;
int height;
} base_clipping;
int palette_index;
void Set(const PipelineShader* s)
{
if (s->cp_AlphaTestValue!=-1)
glUniform1f(s->cp_AlphaTestValue,PT_ALPHA);
if (s->depth_scale!=-1)
glUniform4fv( s->depth_scale, 1, depth_coefs);
if (s->sp_FOG_DENSITY!=-1)
glUniform1f( s->sp_FOG_DENSITY,fog_den_float);
if (s->sp_FOG_COL_RAM!=-1)
glUniform3fv( s->sp_FOG_COL_RAM, 1, ps_FOG_COL_RAM);
if (s->sp_FOG_COL_VERT!=-1)
glUniform3fv( s->sp_FOG_COL_VERT, 1, ps_FOG_COL_VERT);
if (s->fog_clamp_min != -1)
glUniform4fv(s->fog_clamp_min, 1, fog_clamp_min);
if (s->fog_clamp_max != -1)
glUniform4fv(s->fog_clamp_max, 1, fog_clamp_max);
if (s->ndcMat != -1)
glUniformMatrix4fv(s->ndcMat, 1, GL_FALSE, &ndcMat[0][0]);
if (s->palette_index != -1)
glUniform1i(s->palette_index, palette_index);
}
} ShaderUniforms;
class TextureCacheData final : public BaseTextureCacheData
{
public:
TextureCacheData(TSP tsp, TCW tcw) : BaseTextureCacheData(tsp, tcw), texID(glcache.GenTexture()) {
}
TextureCacheData(TextureCacheData&& other) : BaseTextureCacheData(std::move(other)) {
std::swap(texID, other.texID);
}
GLuint texID; //gl texture
std::string GetId() override { return std::to_string(texID); }
void UploadToGPU(int width, int height, const u8 *temp_tex_buffer, bool mipmapped, bool mipmapsIncluded = false) override;
bool Delete() override;
};
class GlTextureCache final : public BaseTextureCache<TextureCacheData>
{
public:
void Cleanup()
{
if (!texturesToDelete.empty())
{
glcache.DeleteTextures((GLsizei)texturesToDelete.size(), &texturesToDelete[0]);
texturesToDelete.clear();
}
CollectCleanup();
}
void DeleteLater(GLuint texId) { texturesToDelete.push_back(texId); }
private:
std::vector<GLuint> texturesToDelete;
};
extern GlTextureCache TexCache;
extern const u32 Zfunction[8];
extern const u32 SrcBlendGL[], DstBlendGL[];
struct OpenGLRenderer : Renderer
{
bool Init() override;
void Term() override;
void Process(TA_context* ctx) override;
bool Render() override;
void RenderFramebuffer(const FramebufferInfo& info) override;
bool RenderLastFrame() override
{
saveCurrentFramebuffer();
bool ret = renderLastFrame();
restoreCurrentFramebuffer();
return ret;
}
void DrawOSD(bool clear_screen) override;
BaseTextureCacheData *GetTexture(TSP tsp, TCW tcw) override;
bool Present() override
{
if (!frameRendered)
return false;
#ifndef LIBRETRO
imguiDriver->setFrameRendered();
#endif
frameRendered = false;
return true;
}
protected:
virtual GLenum getFogTextureSlot() const {
return GL_TEXTURE1;
}
virtual GLenum getPaletteTextureSlot() const {
return GL_TEXTURE2;
}
void saveCurrentFramebuffer() {
#ifdef LIBRETRO
gl.ofbo.origFbo = glsm_get_current_framebuffer();
#else
gl.ofbo.origFbo = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&gl.ofbo.origFbo);
#endif
}
void restoreCurrentFramebuffer() {
glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.origFbo);
}
bool renderLastFrame();
void renderVideoRouting();
private:
bool renderFrame(int width, int height);
protected:
bool frameRendered = false;
int width = 640;
int height = 480;
void initVideoRoutingFrameBuffer();
};
void initQuad();
void termQuad();
void drawQuad(GLuint texId, bool rotate = false, bool swapY = false, float *coords = nullptr);
extern const char* ShaderCompatSource;
extern const char *VertexCompatShader;
extern const char *PixelCompatShader;
class OpenGlSource : public ShaderSource
{
public:
OpenGlSource() : ShaderSource(gl.glsl_version_header) {
addConstant("TARGET_GL", gl.gl_version);
addSource(ShaderCompatSource);
}
};
#ifdef LIBRETRO
extern "C" struct retro_hw_render_callback hw_render;
void termVmuLightgun();
#endif