From d61deae68dafafccbff8540734e171acf7486012 Mon Sep 17 00:00:00 2001 From: Chris Sarbora Date: Sat, 20 Jul 2024 16:19:31 -0500 Subject: [PATCH] add rudimentary shaders and machinery --- cmake/shaders.cmake | 31 ++++++ misc/holder.h | 41 ++++++++ renderer/CMakeLists.txt | 21 +++- renderer/HardwareOpenGL.cpp | 28 +++++- renderer/ShaderProgram.h | 179 +++++++++++++++++++++++++++++++++ renderer/dyna_gl.h | 27 ++++- renderer/shaders/fragment.glsl | 26 +++++ renderer/shaders/vertex.glsl | 29 ++++++ 8 files changed, 371 insertions(+), 11 deletions(-) create mode 100644 cmake/shaders.cmake create mode 100644 misc/holder.h create mode 100644 renderer/ShaderProgram.h create mode 100644 renderer/shaders/fragment.glsl create mode 100644 renderer/shaders/vertex.glsl diff --git a/cmake/shaders.cmake b/cmake/shaders.cmake new file mode 100644 index 00000000..04792dc8 --- /dev/null +++ b/cmake/shaders.cmake @@ -0,0 +1,31 @@ +file(WRITE ${shaders_h} "#pragma once +#include +namespace shaders { +") + +set(idx 0) +set(idx_is_path FALSE) +while(${idx} LESS ${CMAKE_ARGC}) + if(idx_is_path) + set(shader_path ${CMAKE_ARGV${idx}}) + cmake_path(GET shader_path STEM shader_name) + file(READ ${shader_path} content) + file(APPEND ${shaders_h} " +inline constexpr std::string_view ${shader_name} = R\"shader(${content} +)shader\"; +") + else() + if(${CMAKE_ARGV${idx}} STREQUAL "--") + set(idx_is_path TRUE) + endif() + endif() + math(EXPR idx "${idx} + 1") +endwhile() + +foreach(idx RANGE ${CMAKE_ARGC}) +endforeach() + +file(APPEND ${shaders_h} " +} // namespace shaders +") + diff --git a/misc/holder.h b/misc/holder.h new file mode 100644 index 00000000..e6d8e999 --- /dev/null +++ b/misc/holder.h @@ -0,0 +1,41 @@ +/* +* Descent 3 +* Copyright (C) 2024 Descent Developers +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#pragma once + +#include + +template +struct MoveOnlyHolder { + MoveOnlyHolder(T t) + : t_{std::move(t)} {} + + MoveOnlyHolder(MoveOnlyHolder&& other) + : t_{std::exchange(other.t_, {})} {} + + ~MoveOnlyHolder() noexcept { + Deleter(t_); + } + + operator T() const { + return t_; + } + +private: + T t_; +}; diff --git a/renderer/CMakeLists.txt b/renderer/CMakeLists.txt index 6ba3c031..135fc8fa 100644 --- a/renderer/CMakeLists.txt +++ b/renderer/CMakeLists.txt @@ -1,6 +1,21 @@ +cmake_path(SET GENERATED_HEADERS NORMALIZE ${CMAKE_CURRENT_BINARY_DIR}/generated) +cmake_path(SET SHADERS_H NORMALIZE ${GENERATED_HEADERS}/shaders.h) +set(SHADERS + shaders/fragment.glsl + shaders/vertex.glsl +) +add_custom_command( + OUTPUT ${SHADERS_H} + COMMAND ${CMAKE_COMMAND} -Dshaders_h=${SHADERS_H} -P ../cmake/shaders.cmake -- ${SHADERS} + DEPENDS ../cmake/shaders.cmake ${SHADERS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + set(HEADERS dyna_gl.h HardwareInternal.h + ShaderProgram.h + ${SHADERS_H} ) set(CPPS HardwareClipper.cpp @@ -15,12 +30,8 @@ set(CPPS lnxscreenmode.cpp ) -# These are excluded. -#opengl.cpp -#renderer.cpp -#Direct3D.cpp - add_library(renderer STATIC ${HEADERS} ${CPPS}) +target_include_directories(renderer PRIVATE ${GENERATED_HEADERS}) target_link_libraries(renderer PRIVATE SDL2::SDL2 bitmap diff --git a/renderer/HardwareOpenGL.cpp b/renderer/HardwareOpenGL.cpp index d5a4330b..3ad7c8f0 100644 --- a/renderer/HardwareOpenGL.cpp +++ b/renderer/HardwareOpenGL.cpp @@ -21,12 +21,16 @@ #include #include #include +#include #include #if defined(WIN32) #include #endif +#define DECLARE_OPENGL +#include "dyna_gl.h" + #include "byteswap.h" #include "pserror.h" #include "mono.h" @@ -43,9 +47,8 @@ #include "HardwareInternal.h" #include "../Descent3/args.h" #include "NewBitmap.h" - -#define DECLARE_OPENGL -#include "dyna_gl.h" +#include "shaders.h" +#include "ShaderProgram.h" #if defined(WIN32) #include "win/arb_extensions.h" @@ -64,6 +67,21 @@ extern uint8_t Renderer_initted; renderer_type Renderer_type = RENDERER_OPENGL; int WindowGL = 0; +struct Renderer { + Renderer() : shader_{shaders::vertex, shaders::fragment, { + vertexAttrib(3, GL_FLOAT, GL_FALSE, &PosColorUV2Vertex::pos, "in_pos"), + vertexAttrib(4, GL_FLOAT, GL_FALSE, &PosColorUV2Vertex::color, "in_color"), + vertexAttrib(2, GL_FLOAT, GL_FALSE, &PosColorUV2Vertex::uv0, "in_uv0"), + vertexAttrib(2, GL_FLOAT, GL_FALSE, &PosColorUV2Vertex::uv1, "in_uv1") + }} { + shader_.Use(); + } + +private: + ShaderProgram shader_; +}; +std::optional gRenderer; + #ifndef GL_UNSIGNED_SHORT_5_5_5_1 #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 #endif @@ -502,6 +520,8 @@ int opengl_Setup(oeApplication *app, int *width, int *height) { reinterpret_cast(ParentApplication)->set_sizepos(0, 0, *width, *height); } + gRenderer.emplace(); + Already_loaded = 1; return 1; } @@ -700,6 +720,8 @@ void opengl_Close(const bool just_resizing) { mem_free(delete_list); + gRenderer.reset(); + if (GSDLGLContext) { SDL_GL_MakeCurrent(NULL, NULL); SDL_GL_DeleteContext(GSDLGLContext); diff --git a/renderer/ShaderProgram.h b/renderer/ShaderProgram.h new file mode 100644 index 00000000..afc40d0c --- /dev/null +++ b/renderer/ShaderProgram.h @@ -0,0 +1,179 @@ +/* +* Descent 3 +* Copyright (C) 2024 Descent Developers +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#pragma once + +#include +#include +#include +#include +#include "dyna_gl.h" +#include "holder.h" + +template +struct VertexAttrib { + using EnclosingType = E; + GLint size; + GLenum type; + GLboolean normalized; + uintptr_t offset; + std::string name; +}; + +template +VertexAttrib vertexAttrib(GLint size, GLenum type, GLboolean normalized, MemberType EnclosingType::*member, std::string name) { + EnclosingType e; + return VertexAttrib{ + size, + type, + normalized, + reinterpret_cast(&(e.*member)) - reinterpret_cast(&e), + std::move(name) + }; +} + +template +struct VertexBuffer { + VertexBuffer(GLuint program, + std::vector> attribs, + size_t vertex_count, + GLenum bufferType, + V const* initialData = nullptr) + : vao_{outval(dglGenVertexArrays)}, + vbo_{outval(dglGenBuffers)} { + dglBindVertexArray(vao_); + dglBindBuffer(GL_ARRAY_BUFFER, vbo_); + dglBufferData(GL_ARRAY_BUFFER, + vertex_count * sizeof(PosColorUV2Vertex), + initialData, + bufferType); + + for (GLint i{}; i < attribs.size(); i++) { + dglEnableVertexAttribArray(i); + dglVertexAttribPointer(i, + attribs[i].size, + attribs[i].type, + attribs[i].normalized, + sizeof(V), + reinterpret_cast(attribs[i].offset)); + dglBindAttribLocation(program, i, attribs[i].name.c_str()); + } + } + + void UpdateData(size_t vtx_offset, size_t vtx_count, V const* vertices) const { + dglBindBuffer(GL_ARRAY_BUFFER, vbo_); + dglBufferSubData(GL_ARRAY_BUFFER, vtx_offset * sizeof(V), vtx_count * sizeof(V), vertices); + } + +private: + static void DeleteBuffer(GLuint id) { + dglDeleteBuffers(1, &id); + } + static void DeleteVertexArray(GLuint id) { + dglDeleteVertexArrays(1, &id); + } + template + GLuint outval(Generator&& gen) { + GLuint id; + gen(1, &id); + return id; + } + + MoveOnlyHolder vao_; + MoveOnlyHolder vbo_; +}; + +template +struct Shader { + static_assert(kType == GL_VERTEX_SHADER || kType == GL_FRAGMENT_SHADER); + + explicit Shader(std::string_view src) + : id_{dglCreateShader(kType)} { + if (id_ == 0) { + throw std::runtime_error("failed to create shader"); + } + + char const* srcptr = src.data(); + GLint srclen = src.size(); + dglShaderSource(id_, 1, &srcptr, &srclen); + dglCompileShader(id_); + + GLint compile_result; + dglGetShaderiv(id_, GL_COMPILE_STATUS, &compile_result); + if (compile_result != GL_TRUE) { + GLsizei log_length = 0; + GLchar message[1024]; + dglGetShaderInfoLog(id_, 1024, &log_length, message); + throw std::runtime_error(std::string{message, static_cast(log_length)}); + } + } + + GLuint id() const { + ASSERT(id_ != 0); + return id_; + } + +private: + static void DeleteShader(GLuint id) { + dglDeleteShader(id); + } + MoveOnlyHolder id_; +}; + +template +struct ShaderProgram { + explicit ShaderProgram(std::string_view vertexSrc, std::string_view fragmentSrc, std::vector> attribs) + : id_{dglCreateProgram()}, vertex_{vertexSrc}, fragment_{fragmentSrc}, vbo_{id_, std::move(attribs), MAX_POINTS_IN_POLY, GL_DYNAMIC_DRAW} { + if (id_ == 0) { + throw std::runtime_error("error creating GL program"); + } + + dglAttachShader(id_, vertex_.id()); + dglAttachShader(id_, fragment_.id()); + + dglLinkProgram(id_); + + GLint link_result; + dglGetProgramiv(id_, GL_LINK_STATUS, &link_result); + if (link_result != GL_TRUE) + { + GLsizei log_length = 0; + GLchar message[1024]; + dglGetProgramInfoLog(id_, 1024, &log_length, message); + throw std::runtime_error(std::string{message, static_cast(log_length)}); + } + } + + void Use() const { + dglUseProgram(id_); + } + + void Unuse() const { + dglUseProgram(0); + } + +private: + static void DeleteProgram(GLuint id) { + dglDeleteProgram(id); + } + + MoveOnlyHolder id_; + Shader vertex_; + Shader fragment_; + VertexBuffer vbo_; +}; \ No newline at end of file diff --git a/renderer/dyna_gl.h b/renderer/dyna_gl.h index 9a617144..d486f2ba 100644 --- a/renderer/dyna_gl.h +++ b/renderer/dyna_gl.h @@ -146,15 +146,28 @@ static module *LoadOpenGLDLL(const char *dllname) { #define DYNAEXTERN(Name) extern FnPtr const d##Name #endif +DYNAEXTERN(glAttachShader); +DYNAEXTERN(glBindAttribLocation); +DYNAEXTERN(glBindBuffer); DYNAEXTERN(glBindTexture); +DYNAEXTERN(glBindVertexArray); DYNAEXTERN(glBlendFunc); +DYNAEXTERN(glBufferData); +DYNAEXTERN(glBufferSubData); DYNAEXTERN(glClear); DYNAEXTERN(glClearColor); DYNAEXTERN(glColor3ub); DYNAEXTERN(glColor4f); DYNAEXTERN(glColor4ub); DYNAEXTERN(glColorPointer); +DYNAEXTERN(glCompileShader); +DYNAEXTERN(glCreateProgram); +DYNAEXTERN(glCreateShader); +DYNAEXTERN(glDeleteBuffers); +DYNAEXTERN(glDeleteProgram); +DYNAEXTERN(glDeleteShader); DYNAEXTERN(glDeleteTextures); +DYNAEXTERN(glDeleteVertexArrays); DYNAEXTERN(glDepthFunc); DYNAEXTERN(glDepthMask); DYNAEXTERN(glDepthRange); @@ -163,15 +176,23 @@ DYNAEXTERN(glDisableClientState); DYNAEXTERN(glDrawArrays); DYNAEXTERN(glEnable); DYNAEXTERN(glEnableClientState); +DYNAEXTERN(glEnableVertexAttribArray); DYNAEXTERN(glFlush); DYNAEXTERN(glFogf); DYNAEXTERN(glFogfv); DYNAEXTERN(glFogi); +DYNAEXTERN(glGenBuffers); +DYNAEXTERN(glGenVertexArrays); DYNAEXTERN(glGetError); DYNAEXTERN(glGetIntegerv); +DYNAEXTERN(glGetProgramInfoLog); +DYNAEXTERN(glGetProgramiv); +DYNAEXTERN(glGetShaderInfoLog); +DYNAEXTERN(glGetShaderiv); DYNAEXTERN(glGetString); DYNAEXTERN(glGetStringi); DYNAEXTERN(glHint); +DYNAEXTERN(glLinkProgram); DYNAEXTERN(glLoadIdentity); DYNAEXTERN(glLoadMatrixf); DYNAEXTERN(glMatrixMode); @@ -180,6 +201,7 @@ DYNAEXTERN(glPixelStorei); DYNAEXTERN(glPolygonOffset); DYNAEXTERN(glReadPixels); DYNAEXTERN(glScissor); +DYNAEXTERN(glShaderSource); DYNAEXTERN(glTexCoord2f); DYNAEXTERN(glTexCoord4fv); DYNAEXTERN(glTexCoordPointer); @@ -187,9 +209,8 @@ DYNAEXTERN(glTexEnvf); DYNAEXTERN(glTexImage2D); DYNAEXTERN(glTexParameteri); DYNAEXTERN(glTexSubImage2D); -DYNAEXTERN(glVertex2i); -DYNAEXTERN(glVertex3f); -DYNAEXTERN(glVertex3fv); +DYNAEXTERN(glUseProgram); +DYNAEXTERN(glVertexAttribPointer); DYNAEXTERN(glVertexPointer); DYNAEXTERN(glViewport); diff --git a/renderer/shaders/fragment.glsl b/renderer/shaders/fragment.glsl new file mode 100644 index 00000000..afd7f28d --- /dev/null +++ b/renderer/shaders/fragment.glsl @@ -0,0 +1,26 @@ +#version 150 core + +/* +* Descent 3 +* Copyright (C) 2024 Descent Developers +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +out vec4 out_color; + +void main() +{ + out_color = vec4(0); +} \ No newline at end of file diff --git a/renderer/shaders/vertex.glsl b/renderer/shaders/vertex.glsl new file mode 100644 index 00000000..3e346825 --- /dev/null +++ b/renderer/shaders/vertex.glsl @@ -0,0 +1,29 @@ +#version 150 core + +/* +* Descent 3 +* Copyright (C) 2024 Descent Developers +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +in vec3 in_pos; +in vec4 in_color; +in vec2 in_uv0; +in vec2 in_uv1; + +void main() +{ + gl_Position = vec4(0); +} \ No newline at end of file