Scaling filters in SDL

This commit is contained in:
Lior Halphon 2017-12-23 17:29:42 +02:00
parent c03ccba8db
commit d262dde71a
7 changed files with 370 additions and 32 deletions

View File

@ -43,7 +43,7 @@ endif
# Set compilation and linkage flags based on target, platform and configuration
CFLAGS += -Werror -Wall -std=gnu11 -ICore -D_GNU_SOURCE -DVERSION="$(VERSION)" -I. -D_USE_MATH_DEFINES
SDL_LDFLAGS := -lSDL2
SDL_LDFLAGS := -lSDL2 -lGL
ifeq ($(PLATFORM),windows32)
CFLAGS += -IWindows
LDFLAGS += -lmsvcrt -lSDL2main -Wl,/MANIFESTFILE:NUL
@ -56,7 +56,7 @@ SYSROOT := $(shell xcodebuild -sdk macosx -version Path 2> /dev/null)
CFLAGS += -F/Library/Frameworks
OCFLAGS += -x objective-c -fobjc-arc -Wno-deprecated-declarations -isysroot $(SYSROOT) -mmacosx-version-min=10.9
LDFLAGS += -framework AppKit -framework PreferencePanes -framework Carbon -framework QuartzCore
SDL_LDFLAGS := -F/Library/Frameworks -framework SDL2
SDL_LDFLAGS := -F/Library/Frameworks -framework SDL2 -framework OpenGL
endif
CFLAGS += -Wno-deprecated-declarations
ifeq ($(PLATFORM),windows32)
@ -88,7 +88,7 @@ endif
cocoa: $(BIN)/SameBoy.app
quicklook: $(BIN)/SameBoy.qlgenerator
sdl: $(SDL_TARGET) $(BIN)/SDL/dmg_boot.bin $(BIN)/SDL/cgb_boot.bin $(BIN)/SDL/LICENSE $(BIN)/SDL/registers.sym $(BIN)/SDL/background.bmp
sdl: $(SDL_TARGET) $(BIN)/SDL/dmg_boot.bin $(BIN)/SDL/cgb_boot.bin $(BIN)/SDL/LICENSE $(BIN)/SDL/registers.sym $(BIN)/SDL/background.bmp $(BIN)/SDL/Shaders
bootroms: $(BIN)/BootROMs/cgb_boot.bin $(BIN)/BootROMs/dmg_boot.bin
tester: $(TESTER_TARGET) $(BIN)/tester/dmg_boot.bin $(BIN)/tester/cgb_boot.bin
all: cocoa sdl tester
@ -262,14 +262,21 @@ $(BIN)/SameBoy.app/Contents/Resources/%.bin: $(BOOTROMS_DIR)/%.bin
cp -f $^ $@
$(BIN)/SDL/LICENSE: LICENSE
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
$(BIN)/SDL/registers.sym: Misc/registers.sym
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
$(BIN)/SDL/background.bmp: SDL/background.bmp
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
$(BIN)/SDL/Shaders: Shaders
-@$(MKDIR) -p $(dir $@)
cp -rf $^ $@
# Boot ROMs
$(BIN)/BootROMs/%.bin: BootROMs/%.asm

132
SDL/gui.c
View File

@ -20,6 +20,31 @@ unsigned command_parameter;
#define MODIFIER_NAME CTRL_STRING
#endif
shader_t shader;
SDL_Rect rect;
void render_texture(void *pixels, void *previous)
{
if (renderer) {
if (pixels) {
SDL_UpdateTexture(texture, NULL, pixels, 160 * sizeof (uint32_t));
}
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
}
else {
static void *_pixels = NULL;
if (pixels) {
_pixels = pixels;
}
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
render_bitmap_with_shader(&shader, _pixels, previous, rect.x, rect.y, rect.w, rect.h);
SDL_GL_SwapWindow(window);
}
}
configuration_t configuration =
{
{ SDL_SCANCODE_RIGHT,
@ -59,7 +84,7 @@ static const char *help[] ={
void update_viewport(void)
{
int win_width, win_height;
SDL_GetWindowSize(window, &win_width, &win_height);
SDL_GL_GetDrawableSize(window, &win_width, &win_height);
double x_factor = win_width / 160.0;
double y_factor = win_height / 144.0;
@ -80,9 +105,15 @@ void update_viewport(void)
unsigned new_width = x_factor * 160;
unsigned new_height = y_factor * 144;
SDL_Rect rect = (SDL_Rect){(win_width - new_width) / 2, (win_height - new_height) /2,
rect = (SDL_Rect){(win_width - new_width) / 2, (win_height - new_height) /2,
new_width, new_height};
SDL_RenderSetViewport(renderer, &rect);
if (renderer) {
SDL_RenderSetViewport(renderer, &rect);
}
else {
glViewport(rect.x, rect.y, rect.w, rect.h);
}
}
/* Does NOT check for bounds! */
@ -234,9 +265,7 @@ void cycle_scaling(unsigned index)
configuration.scaling_mode = 0;
}
update_viewport();
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
render_texture(NULL, NULL);
}
void cycle_scaling_backwards(unsigned index)
@ -248,9 +277,7 @@ void cycle_scaling_backwards(unsigned index)
configuration.scaling_mode--;
}
update_viewport();
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
render_texture(NULL, NULL);
}
static void cycle_color_correction(unsigned index)
@ -273,6 +300,82 @@ static void cycle_color_correction_backwards(unsigned index)
}
}
struct shader_name {
const char *file_name;
const char *display_name;
} shaders[] =
{
{"NearestNeighbor", "Nearest Neighbor"},
{"Bilinear", "Bilinear"},
{"SmoothBilinear", "Smooth Bilinear"},
{"Scale2x", "Scale2x"},
{"Scale4x", "Scale4x"},
{"AAScale2x", "Anti-aliased Scale2x"},
{"AAScale4x", "Anti-aliased Scale4x"},
{"HQ2x", "HQ2x"},
{"OmniScale", "OmniScale"},
{"OmniScaleLegacy", "OmniScale Legacy"},
{"AAOmniScaleLegacy", "AA OmniScale Legacy"},
};
static void cycle_filter(unsigned index)
{
unsigned i = 0;
for (; i < sizeof(shaders) / sizeof(shaders[0]); i++) {
if (strcmp(shaders[i].file_name, configuration.filter) == 0) {
break;
}
}
i += 1;
if (i >= sizeof(shaders) / sizeof(shaders[0])) {
i -= sizeof(shaders) / sizeof(shaders[0]);
}
strcpy(configuration.filter, shaders[i].file_name);
free_shader(&shader);
if (!init_shader_with_name(&shader, configuration.filter)) {
init_shader_with_name(&shader, "NearestNeighbor");
}
}
static void cycle_filter_backwards(unsigned index)
{
unsigned i = 0;
for (; i < sizeof(shaders) / sizeof(shaders[0]); i++) {
if (strcmp(shaders[i].file_name, configuration.filter) == 0) {
break;
}
}
i -= 1;
if (i >= sizeof(shaders) / sizeof(shaders[0])) {
i = sizeof(shaders) / sizeof(shaders[0]) - 1;
}
strcpy(configuration.filter, shaders[i].file_name);
free_shader(&shader);
if (!init_shader_with_name(&shader, configuration.filter)) {
init_shader_with_name(&shader, "NearestNeighbor");
}
}
const char *current_filter_name(unsigned index)
{
unsigned i = 0;
for (; i < sizeof(shaders) / sizeof(shaders[0]); i++) {
if (strcmp(shaders[i].file_name, configuration.filter) == 0) {
break;
}
}
if (i == sizeof(shaders) / sizeof(shaders[0])) {
i = 0;
}
return shaders[i].display_name;
}
static void return_to_root_menu(unsigned index)
{
@ -282,6 +385,7 @@ static void return_to_root_menu(unsigned index)
static const struct menu_item graphics_menu[] = {
{"Scaling Mode:", cycle_scaling, current_scaling_mode, cycle_scaling_backwards},
{"Scaling Filter:", cycle_filter, current_filter_name, cycle_filter_backwards},
{"Color Correction:", cycle_color_correction, current_color_correction_mode, cycle_color_correction_backwards},
{"Back", return_to_root_menu},
{NULL,}
@ -399,6 +503,7 @@ static void detect_joypad_layout(unsigned index)
static const struct menu_item joypad_menu[] = {
{"Joypad:", cycle_joypads, current_joypad_name, cycle_joypads_backwards},
{"Detect layout", detect_joypad_layout},
{"Back", return_to_root_menu},
{NULL,}
};
@ -510,9 +615,7 @@ void run_gui(bool is_running)
case SDL_WINDOWEVENT: {
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
update_viewport();
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
render_texture(NULL, NULL);
}
break;
}
@ -667,10 +770,7 @@ void run_gui(bool is_running)
break;
}
SDL_UpdateTexture(texture, NULL, pixels, 160 * sizeof (uint32_t));
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
render_texture(pixels, NULL);
}
} while (SDL_WaitEvent(&event));
}

View File

@ -3,11 +3,13 @@
#include <SDL2/SDL.h>
#include <Core/gb.h>
#include "shader.h"
extern SDL_Window *window;
extern SDL_Renderer *renderer;
extern SDL_Texture *texture;
extern SDL_PixelFormat *pixel_format;
extern shader_t shader;
enum scaling_mode {
GB_SDL_SCALING_ENTIRE_WINDOW,
@ -37,6 +39,8 @@ typedef struct {
bool div_joystick;
bool flip_joystick_bit_1;
bool swap_joysticks_bits_1_and_2;
char filter[32];
} configuration_t;
extern configuration_t configuration;
@ -44,5 +48,6 @@ extern configuration_t configuration;
void update_viewport(void);
void run_gui(bool is_running);
unsigned fix_joypad_button(unsigned button);
void render_texture(void *pixels, void *previous);
#endif

View File

@ -4,6 +4,7 @@
#include "gb.h"
#include "utils.h"
#include "gui.h"
#include "shader.h"
#ifndef _WIN32
#define AUDIO_FREQUENCY 96000
@ -212,10 +213,7 @@ static void handle_events(GB_gameboy_t *gb)
static void vblank(GB_gameboy_t *gb)
{
SDL_UpdateTexture(texture, NULL, pixels, 160 * sizeof (uint32_t));
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
render_texture(pixels, NULL);
handle_events(gb);
}
@ -390,14 +388,24 @@ usage:
SDL_Init( SDL_INIT_EVERYTHING );
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
window = SDL_CreateWindow("SameBoy v" xstr(VERSION), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
160 * 2, 144 * 2, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
160 * 2, 144 * 2, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_SetWindowMinimumSize(window, 160, 144);
renderer = SDL_CreateRenderer(window, -1, 0);
texture = SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), SDL_TEXTUREACCESS_STREAMING, 160, 144);
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
if (gl_context == NULL) {
renderer = SDL_CreateRenderer(window, -1, 0);
texture = SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), SDL_TEXTUREACCESS_STREAMING, 160, 144);
pixel_format = SDL_AllocFormat(SDL_GetWindowPixelFormat(window));
}
else {
pixel_format = SDL_AllocFormat(SDL_PIXELFORMAT_ABGR8888);
}
pixel_format = SDL_AllocFormat(SDL_GetWindowPixelFormat(window));
/* Configure Audio */
memset(&want_aspec, 0, sizeof(want_aspec));
@ -420,6 +428,11 @@ usage:
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
if (!init_shader_with_name(&shader, configuration.filter)) {
init_shader_with_name(&shader, "NearestNeighbor");
}
update_viewport();
if (filename == NULL) {
run_gui(false);
}

184
SDL/shader.c Normal file
View File

@ -0,0 +1,184 @@
#include <stdio.h>
#include <string.h>
#include "shader.h"
#include "utils.h"
#include <OpenGL/gl3.h>
static const char *vertex_shader = "\n\
#version 150 \n\
in vec4 aPosition;\n\
void main(void) {\n\
gl_Position = aPosition;\n\
}\n\
";
static GLuint create_shader(const char *source, GLenum type)
{
// Create the shader object
GLuint shader = glCreateShader(type);
// Load the shader source
glShaderSource(shader, 1, &source, 0);
// Compile the shader
glCompileShader(shader);
// Check for errors
GLint status = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
GLchar messages[1024];
glGetShaderInfoLog(shader, sizeof(messages), 0, &messages[0]);
fprintf(stderr, "GLSL Shader Error: %s", messages);
}
return shader;
}
static GLuint create_program(const char *vsh, const char *fsh)
{
// Build shaders
GLuint vertex_shader = create_shader(vsh, GL_VERTEX_SHADER);
GLuint fragment_shader = create_shader(fsh, GL_FRAGMENT_SHADER);
// Create program
GLuint program = glCreateProgram();
// Attach shaders
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
// Link program
glLinkProgram(program);
// Check for errors
GLint status;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
GLchar messages[1024];
glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]);
fprintf(stderr, "GLSL Program Error: %s", messages);
}
// Delete shaders
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
return program;
}
bool init_shader_with_name(shader_t *shader, const char *name)
{
static char master_shader_code[0x801] = {0,};
static char shader_code[0x10001] = {0,};
static char final_shader_code[0x10801] = {0,};
static ssize_t filter_token_location = 0;
if (!master_shader_code[0]) {
FILE *master_shader_f = fopen(executable_relative_path("Shaders/MasterShader.fsh"), "r");
if (!master_shader_f) return false;
fread(master_shader_code, 1, sizeof(master_shader_code) - 1, master_shader_f);
fclose(master_shader_f);
filter_token_location = strstr(master_shader_code, "{filter}") - master_shader_code;
if (filter_token_location < 0) {
master_shader_code[0] = 0;
return false;
}
}
char shader_path[1024];
sprintf(shader_path, "Shaders/%s.fsh", name);
FILE *shader_f = fopen(executable_relative_path(shader_path), "r");
if (!shader_f) return false;
memset(shader_code, 0, sizeof(shader_code));
fread(shader_code, 1, sizeof(shader_code) - 1, shader_f);
fclose(shader_f);
memset(final_shader_code, 0, sizeof(final_shader_code));
memcpy(final_shader_code, master_shader_code, filter_token_location);
strcpy(final_shader_code + filter_token_location, shader_code);
strcat(final_shader_code + filter_token_location,
master_shader_code + filter_token_location + sizeof("{filter}") - 1);
shader->program = create_program(vertex_shader, final_shader_code);
// Attributes
shader->position_attribute = glGetAttribLocation(shader->program, "aPosition");
// Uniforms
shader->resolution_uniform = glGetUniformLocation(shader->program, "uResolution");
shader->origin_uniform = glGetUniformLocation(shader->program, "uOrigin");
glGenTextures(1, &shader->texture);
glBindTexture(GL_TEXTURE_2D, shader->texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
shader->texture_uniform = glGetUniformLocation(shader->program, "image");
glGenTextures(1, &shader->previous_texture);
glBindTexture(GL_TEXTURE_2D, shader->previous_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
shader->previous_texture_uniform = glGetUniformLocation(shader->program, "previousImage");
shader->mix_previous_uniform = glGetUniformLocation(shader->program, "uMixPrevious");
// Program
glUseProgram(shader->program);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLuint vbo;
glGenBuffers(1, &vbo);
// Attributes
static GLfloat const quad[16] = {
-1.f, -1.f, 0, 1,
-1.f, +1.f, 0, 1,
+1.f, -1.f, 0, 1,
+1.f, +1.f, 0, 1,
};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW);
glEnableVertexAttribArray(shader->position_attribute);
glVertexAttribPointer(shader->position_attribute, 4, GL_FLOAT, GL_FALSE, 0, 0);
return true;
}
void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,unsigned x, unsigned y, unsigned w, unsigned h)
{
glUseProgram(shader->program);
glUniform2f(shader->origin_uniform, x, y);
glUniform2f(shader->resolution_uniform, w, h);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, shader->texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 160, 144, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
glUniform1i(shader->texture_uniform, 0);
glUniform1i(shader->mix_previous_uniform, previous != NULL);
if (previous) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, shader->previous_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 160, 144, 0, GL_RGBA, GL_UNSIGNED_BYTE, previous);
glUniform1i(shader->previous_texture_uniform, 1);
}
glBindFragDataLocation(shader->program, 0, "frag_color");
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
void free_shader(shader_t *shader)
{
glDeleteProgram(shader->program);
glDeleteTextures(1, &shader->texture);
glDeleteTextures(1, &shader->previous_texture);
}

23
SDL/shader.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef shader_h
#define shader_h
#include <OpenGL/gl3.h>
#include <stdbool.h>
typedef struct shader_s {
GLuint resolution_uniform;
GLuint origin_uniform;
GLuint texture_uniform;
GLuint previous_texture_uniform;
GLuint mix_previous_uniform;
GLuint position_attribute;
GLuint texture;
GLuint previous_texture;
GLuint program;
} shader_t;
bool init_shader_with_name(shader_t *shader, const char *name);
void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,unsigned x, unsigned y, unsigned w, unsigned h);
void free_shader(struct shader_s *shader);
#endif /* shader_h */

View File

@ -4,14 +4,20 @@ uniform sampler2D previousImage;
uniform bool uMixPrevious;
uniform vec2 uResolution;
uniform vec2 uOrigin;
const vec2 textureDimensions = vec2(160, 144);
out vec4 frag_color;
vec4 modified_frag_cord;
#define gl_FragCoord modified_frag_cord
#line 1
{filter}
#undef gl_FragCoord
void main() {
modified_frag_cord = gl_FragCoord - vec4(uOrigin.x, uOrigin.y, 0, 0);
if (uMixPrevious) {
frag_color = mix(scale(image), scale(previousImage), 0.5);
}