*very* experimental shader support

havent tested it yet, beware
This commit is contained in:
/nick haya 2022-02-17 09:34:01 +08:00
parent e4dc4a8331
commit d2be0d9e2b
3 changed files with 221 additions and 2 deletions

View File

@ -35,6 +35,7 @@
#include "guicon.h"
#include "sdfml/music.hpp"
#include "sdfml/shader.hpp"
#include "SDL_gpu/SDL_gpu.h"
@ -100,6 +101,8 @@ inline std::string _GetCurrentDirectory()
namespace sdfml {
static double elapsed;
inline int llog(string prefix, string msg, lLOG_TYPE type = NORMAL, string file = "???.cpp", int line = 0) {
clock_t now = std::clock();
@ -187,6 +190,8 @@ namespace sdfml {
Vector3<Uint8> color;
ShaderProg shader;
virtual void create(int x, int y, string path) {
this->x = x;
this->y = y;
@ -198,6 +203,9 @@ namespace sdfml {
color.g = 255;
color.b = 255;
}
virtual void addShader(std::string path) {
shader.loadShader(path, FRAGMENT, mContext.gpu_render, _tex_gpu);
}
GPU_Rect *r;
virtual void update(float elapsed) {
_x = x+offset.x;
@ -206,14 +214,17 @@ namespace sdfml {
r = &_src_rect;
if (r->w == 0)
r = NULL;
shader.updateShader(static_cast<float>(sdfml::elapsed));
GPU_Rect dst = {static_cast<float>(_x-_camera->x), static_cast<float>(_y-_camera->y), width*scale.x, height*scale.y};
GPU_BlitRectX(_tex_gpu, r, mContext.gpu_render, &dst, angle, NULL, NULL, GPU_FLIP_NONE);
shader.postUpdate();
}
virtual void destroy() {
_x = 0;
_y = 0;
_w = 0;
_h = 0;
shader.freeShader();
GPU_FreeImage(_tex_gpu);
}
virtual void updateCamera(SDL_Rect* camera) {
@ -466,8 +477,6 @@ namespace sdfml {
return FRAMERATE*time;
}
static double elapsed;
inline int update() {
int lastUpdate = SDL_GetTicks();
bool run = true;

179
src/sdfml/shader.cpp Normal file
View File

@ -0,0 +1,179 @@
#include "shader.hpp"
// Loads a shader and prepends version/compatibility info before compiling it.
// Normally, you can just use GPU_LoadShader() for shader source files or GPU_CompileShader() for strings.
// However, some hardware (certain ATI/AMD cards) does not let you put non-#version preprocessing at the top of the file.
// Therefore, I need to prepend the version info here so I can support both GLSL and GLSLES with one shader file.
Uint32 loadShaderFromFile(GPU_ShaderEnum shader_type, const char* filename)
{
SDL_RWops* rwops;
Uint32 shader;
char* source;
int header_size, file_size;
const char* header = "";
GPU_Renderer* renderer = GPU_GetCurrentRenderer();
// Open file
rwops = SDL_RWFromFile(filename, "rb");
if(rwops == NULL)
{
GPU_PushErrorCode("load_shader", GPU_ERROR_FILE_NOT_FOUND, "Shader file \"%s\" not found", filename);
return 0;
}
// Get file size
file_size = SDL_RWseek(rwops, 0, SEEK_END);
SDL_RWseek(rwops, 0, SEEK_SET);
// Get size from header
if(renderer->shader_language == GPU_LANGUAGE_GLSL)
{
if(renderer->max_shader_version >= 120)
header = "#version 120\n";
else
header = "#version 110\n"; // Maybe this is good enough?
}
else if(renderer->shader_language == GPU_LANGUAGE_GLSLES)
header = "#version 100\nprecision mediump int;\nprecision mediump float;\n";
header_size = strlen(header);
// Allocate source buffer
source = (char*)malloc(sizeof(char)*(header_size + file_size + 1));
// Prepend header
strcpy(source, header);
// Read in source code
SDL_RWread(rwops, source + strlen(source), 1, file_size);
source[header_size + file_size] = '\0';
// Compile the shader
shader = GPU_CompileShader(shader_type, source);
// Clean up
free(source);
SDL_RWclose(rwops);
return shader;
}
Uint32 _loadShader(GPU_ShaderEnum shader_type, std::string source) {
Uint32 shader;
std::string header = "";
GPU_Renderer* renderer = GPU_GetCurrentRenderer();
// Get size from header
if(renderer->shader_language == GPU_LANGUAGE_GLSL)
{
if(renderer->max_shader_version >= 120)
header = "#version 120\n";
else
header = "#version 110\n"; // Maybe this is good enough?
}
else if(renderer->shader_language == GPU_LANGUAGE_GLSLES)
header = "#version 100\nprecision mediump int;\nprecision mediump float;\n";
std::string final_source = header + source;
// Compile the shader
shader = GPU_CompileShader(shader_type, final_source.c_str());
return shader;
}
void sdfml::ShaderProg::loadShader(std::string path, SHADER_TYPE shader_type, GPU_Target* target, GPU_Image* image) {
Uint32 s;
switch (shader_type) {
case FRAGMENT:
s = loadShaderFromFile(GPU_FRAGMENT_SHADER, path.c_str());
frag_int = GPU_LinkShaderProgram(s);
fragment = GPU_LoadShaderBlock(frag_int, "gpu_Vertex", "gpu_TexCoord", "gpu_Color", "gpu_ModelViewProjectionMatrix");
GPU_ActivateShaderProgram(frag_int, &fragment);
if(image != NULL)
{
GPU_SetUniformf(GPU_GetUniformLocation(frag_int, "resolution_x"), image->w);
GPU_SetUniformf(GPU_GetUniformLocation(frag_int, "resolution_y"), image->h);
}
GPU_SetUniformf(GPU_GetUniformLocation(frag_int, "screen_w"), target->w);
GPU_SetUniformf(GPU_GetUniformLocation(frag_int, "screen_h"), target->h);
iTime = GPU_GetUniformLocation(frag_int, "time");
break;
case VERTEX:
s = loadShaderFromFile(GPU_VERTEX_SHADER, path.c_str());
vert_int = GPU_LinkShaderProgram(s);
vertex = GPU_LoadShaderBlock(vert_int, "gpu_Vertex", "gpu_TexCoord", "gpu_Color", "gpu_ModelViewProjectionMatrix");
GPU_ActivateShaderProgram(vert_int, &vertex);
if(image != NULL)
{
GPU_SetUniformf(GPU_GetUniformLocation(vert_int, "resolution_x"), image->w);
GPU_SetUniformf(GPU_GetUniformLocation(vert_int, "resolution_y"), image->h);
}
GPU_SetUniformf(GPU_GetUniformLocation(vert_int, "screen_w"), target->w);
GPU_SetUniformf(GPU_GetUniformLocation(vert_int, "screen_h"), target->h);
iTime = GPU_GetUniformLocation(vert_int, "time");
break;
}
}
void sdfml::ShaderProg::loadShaderFromString(std::string source, SHADER_TYPE shader_type, GPU_Target* target, GPU_Image* image) {
Uint32 s;
switch (shader_type) {
case FRAGMENT:
s = _loadShader(GPU_FRAGMENT_SHADER, source);
frag_int = GPU_LinkShaderProgram(s);
fragment = GPU_LoadShaderBlock(frag_int, "gpu_Vertex", "gpu_TexCoord", "gpu_Color", "gpu_ModelViewProjectionMatrix");
GPU_ActivateShaderProgram(frag_int, &fragment);
if(image != NULL)
{
GPU_SetUniformf(GPU_GetUniformLocation(frag_int, "resolution_x"), image->w);
GPU_SetUniformf(GPU_GetUniformLocation(frag_int, "resolution_y"), image->h);
}
GPU_SetUniformf(GPU_GetUniformLocation(frag_int, "screen_w"), target->w);
GPU_SetUniformf(GPU_GetUniformLocation(frag_int, "screen_h"), target->h);
iTime = GPU_GetUniformLocation(frag_int, "time");
break;
case VERTEX:
s = _loadShader(GPU_VERTEX_SHADER, source);
vert_int = GPU_LinkShaderProgram(s);
vertex = GPU_LoadShaderBlock(vert_int, "gpu_Vertex", "gpu_TexCoord", "gpu_Color", "gpu_ModelViewProjectionMatrix");
GPU_ActivateShaderProgram(vert_int, &vertex);
if(image != NULL)
{
GPU_SetUniformf(GPU_GetUniformLocation(vert_int, "resolution_x"), image->w);
GPU_SetUniformf(GPU_GetUniformLocation(vert_int, "resolution_y"), image->h);
}
GPU_SetUniformf(GPU_GetUniformLocation(vert_int, "screen_w"), target->w);
GPU_SetUniformf(GPU_GetUniformLocation(vert_int, "screen_h"), target->h);
iTime = GPU_GetUniformLocation(vert_int, "time");
break;
}
}
void sdfml::ShaderProg::freeShader() {
if (frag_int != NULL)
GPU_FreeShaderProgram(frag_int);
if (vert_int != NULL)
GPU_FreeShaderProgram(vert_int);
}
void sdfml::ShaderProg::updateShader(float elapsed) {
if (frag_int != NULL)
GPU_ActivateShaderProgram(frag_int, &fragment);
if (vert_int != NULL)
GPU_ActivateShaderProgram(vert_int, &vertex);
if (frag_int != NULL || vert_int != NULL)
GPU_SetUniformf(iTime, elapsed);
}
void sdfml::ShaderProg::postUpdate() {
GPU_ActivateShaderProgram(0, NULL);
}

31
src/sdfml/shader.hpp Normal file
View File

@ -0,0 +1,31 @@
#ifndef _SHADER_HPP
#define _SHADER_HPP
#include "../SDL2/SDL.h"
#include "../SDL_gpu/SDL_gpu.h"
#include <string>
enum SHADER_TYPE {
FRAGMENT,
VERTEX
};
namespace sdfml {
class ShaderProg {
public:
GPU_ShaderBlock fragment;
GPU_ShaderBlock vertex;
void loadShader(std::string path, SHADER_TYPE shader_type, GPU_Target* target, GPU_Image* image);
void loadShaderFromString(std::string source, SHADER_TYPE shader_type, GPU_Target* target, GPU_Image* image);
void freeShader();
void updateShader(float elapsed);
void postUpdate();
protected:
Uint32 frag_int = NULL;
Uint32 vert_int = NULL;
int iTime = 0;
};
}
#endif