From 42dba1148dfdff61028c23e5df04519787c35f7f Mon Sep 17 00:00:00 2001 From: Luke Street Date: Wed, 5 May 2021 19:43:30 -0400 Subject: [PATCH] WIP Metal backend for VISIGen --- visigen/CMakeLists.txt | 23 +- visigen/MainMac.mm | 141 ++------- visigen/MainWin.cpp | 4 +- visigen/MainXlib.cpp | 4 +- visigen/Shader.metal | 68 +++++ visigen/ShaderTypes.h | 32 +++ visigen/VISIBuilder.cpp | 34 ++- visigen/VISIBuilder.hpp | 6 +- visigen/VISIRenderer.cpp | 506 ++++++++------------------------- visigen/VISIRenderer.hpp | 52 ++-- visigen/VISIRendererMetal.hh | 26 ++ visigen/VISIRendererMetal.mm | 480 +++++++++++++++++++++++++++++++ visigen/VISIRendererOpenGL.cpp | 426 +++++++++++++++++++++++++++ visigen/VISIRendererOpenGL.hpp | 42 +++ 14 files changed, 1283 insertions(+), 561 deletions(-) create mode 100644 visigen/Shader.metal create mode 100644 visigen/ShaderTypes.h create mode 100644 visigen/VISIRendererMetal.hh create mode 100644 visigen/VISIRendererMetal.mm create mode 100644 visigen/VISIRendererOpenGL.cpp create mode 100644 visigen/VISIRendererOpenGL.hpp diff --git a/visigen/CMakeLists.txt b/visigen/CMakeLists.txt index f08f4ff1e..46660564b 100644 --- a/visigen/CMakeLists.txt +++ b/visigen/CMakeLists.txt @@ -7,7 +7,6 @@ if (NOT MSVC) set(CMAKE_CXX_STANDARD_REQUIRED ON) endif() - add_executable(visigen VISIBuilder.cpp VISIBuilder.hpp @@ -29,17 +28,21 @@ else() endif() if(APPLE) - target_sources(visigen PRIVATE MainMac.mm) + target_sources(visigen PRIVATE MainMac.mm VISIRendererMetal.mm VISIRendererMetal.hh) set_source_files_properties(MainMac.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) + set_source_files_properties(VISIRendererMetal.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) + find_library(METAL_LIBRARY Metal REQUIRED) + target_link_libraries(visigen PRIVATE ${METAL_LIBRARY}) elseif(WIN32) - target_sources(visigen PRIVATE MainWin.cpp) + target_sources(visigen PRIVATE + MainWin.cpp + VISIRendererOpenGL.cpp + VISIRendererOpenGL.hpp) else() - target_sources(visigen PRIVATE MainXlib.cpp) -endif() - -if(APPLE) - find_library(OPENGL_LIBRARY OpenGL) - target_link_libraries(visigen PRIVATE ${OPENGL_LIBRARY}) + target_sources(visigen PRIVATE + MainXlib.cpp + VISIRendererOpenGL.cpp + VISIRendererOpenGL.hpp) endif() target_link_libraries(visigen PRIVATE @@ -50,6 +53,8 @@ target_link_libraries(visigen PRIVATE lzokay xxhash zeus + png ${ZLIB_LIBRARIES} ) +target_include_directories(visigen PRIVATE ${PNG_INCLUDE_DIR}) endif() diff --git a/visigen/MainMac.mm b/visigen/MainMac.mm index 4b53365ee..003dde4ff 100644 --- a/visigen/MainMac.mm +++ b/visigen/MainMac.mm @@ -1,138 +1,33 @@ -#include "VISIRenderer.hpp" -#include +#include "../version.h" +#include "VISIRendererMetal.hh" #include "athena/Global.hpp" #include "logvisor/logvisor.hpp" -#include "../version.h" +#include +#include #include #if !__has_feature(objc_arc) #error ARC Required #endif -static std::thread s_task; - -static const NSOpenGLPixelFormatAttribute PF_RGBA8_Z24_ATTRS[] = -{ - NSOpenGLPFAAccelerated, - NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, - //NSOpenGLPFADoubleBuffer, - NSOpenGLPFAColorSize, 24, - NSOpenGLPFAAlphaSize, 8, - NSOpenGLPFADepthSize, 24, - 0, 0 -}; - -@interface OpenGLView : NSOpenGLView -{ - VISIRenderer* m_renderer; -} -- (id)initWithFrame:(NSRect)frame renderer:(VISIRenderer*)renderer; -@end - -static NSWindow* s_Window; -static void UpdatePercent(float percent) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - s_Window.title = [NSString stringWithFormat:@"VISIGen [%g%%]", percent * 100.f]; - }); -} - -@implementation OpenGLView -- (id)initWithFrame:(NSRect)frame renderer:(VISIRenderer*)renderer; -{ - NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:PF_RGBA8_Z24_ATTRS]; - self = [super initWithFrame:frame pixelFormat:pf]; - m_renderer = renderer; - return self; -} -- (void)prepareOpenGL -{ - [super prepareOpenGL]; - s_task = std::thread([self](){ - [[self openGLContext] makeCurrentContext]; - m_renderer->Run(UpdatePercent); - [NSApp terminate:nil]; - }); -} -@end - -@interface AppDelegate : NSObject -{ - VISIRenderer* m_renderer; - NSWindow* m_window; - NSOpenGLView* m_glView; - int m_instanceIdx; -} -- (id)initWithRenderer:(VISIRenderer*)renderer instIdx:(int)instIdx; -@end - -@implementation AppDelegate -- (id)initWithRenderer:(VISIRenderer*)renderer instIdx:(int)instIdx -{ - self = [super init]; - m_renderer = renderer; - m_instanceIdx = instIdx; - return self; -} -- (void)applicationDidFinishLaunching:(NSNotification*)notification -{ - int x = 0; - int y = 0; - if (m_instanceIdx != -1) - { - x = (m_instanceIdx & 1) != 0; - y = (m_instanceIdx & 2) != 0; - } - NSRect cRect = NSMakeRect(x * 768, y * 534, 768, 512); - m_window = [[NSWindow alloc] initWithContentRect:cRect - styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable - backing:NSBackingStoreBuffered - defer:NO]; - m_window.releasedWhenClosed = NO; - m_window.title = @"VISIGen"; - s_Window = m_window; - m_glView = [[OpenGLView alloc] initWithFrame:cRect renderer:m_renderer]; - m_window.contentView = m_glView; - [m_window makeKeyAndOrderFront:nil]; -} -- (void)applicationWillTerminate:(NSNotification*)notification -{ - m_renderer->Terminate(); - if (s_task.joinable()) - s_task.join(); - exit(m_renderer->ReturnVal()); -} -@end - static logvisor::Module AthenaLog("Athena"); -static void AthenaExc(athena::error::Level level, const char* /*file*/, const char*, int /*line*/, +static void AthenaExc(athena::error::Level level, const char * /*file*/, const char *, int /*line*/, fmt::string_view fmt, fmt::format_args args) { AthenaLog.vreport(logvisor::Level(level), fmt, args); } -int main(int argc, const char** argv) -{ - if (argc > 1 && !strcmp(argv[1], "--dlpackage")) - { - fmt::print(FMT_STRING("{}\n"), METAFORCE_DLPACKAGE); - return 100; - } - - logvisor::RegisterStandardExceptions(); - logvisor::RegisterConsoleLogger(); - atSetExceptionHandler(AthenaExc); - VISIRenderer renderer(argc, argv); - int instIdx = -1; - if (argc > 3) - instIdx = atoi(argv[3]); - @autoreleasepool - { - [[NSApplication sharedApplication] setActivationPolicy:NSApplicationActivationPolicyRegular]; +int main(int argc, const char **argv) { + if (argc > 1 && !strcmp(argv[1], "--dlpackage")) { + fmt::print(FMT_STRING("{}\n"), METAFORCE_DLPACKAGE); + return 100; + } - /* Delegate (OS X callbacks) */ - AppDelegate* appDelegate = [[AppDelegate alloc] initWithRenderer:&renderer instIdx:instIdx]; - [[NSApplication sharedApplication] setDelegate:appDelegate]; - [[NSApplication sharedApplication] run]; - } - return renderer.ReturnVal(); + logvisor::RegisterStandardExceptions(); + logvisor::RegisterConsoleLogger(); + atSetExceptionHandler(AthenaExc); + VISIRendererMetal renderer(argc, argv); + @autoreleasepool { + renderer.Run(nullptr); + } + return renderer.ReturnVal(); } diff --git a/visigen/MainWin.cpp b/visigen/MainWin.cpp index 14f4a000c..e170c94f6 100644 --- a/visigen/MainWin.cpp +++ b/visigen/MainWin.cpp @@ -1,4 +1,4 @@ -#include "VISIRenderer.hpp" +#include "VISIRendererOpenGL.hpp" #include #include #include @@ -49,7 +49,7 @@ int wmain(int argc, const hecl::SystemChar** argv) { logvisor::RegisterStandardExceptions(); logvisor::RegisterConsoleLogger(); atSetExceptionHandler(AthenaExc); - VISIRenderer renderer(argc, argv); + VISIRendererOpenGL renderer(argc, argv); s_Renderer = &renderer; int instIdx = -1; diff --git a/visigen/MainXlib.cpp b/visigen/MainXlib.cpp index 1792064c7..30147325f 100644 --- a/visigen/MainXlib.cpp +++ b/visigen/MainXlib.cpp @@ -1,4 +1,4 @@ -#include "VISIRenderer.hpp" +#include "VISIRendererOpenGL.hpp" #include #include #include @@ -86,7 +86,7 @@ int main(int argc, const char** argv) { logvisor::RegisterStandardExceptions(); logvisor::RegisterConsoleLogger(); atSetExceptionHandler(AthenaExc); - VISIRenderer renderer(argc, argv); + VISIRendererOpenGL renderer(argc, argv); if (!XInitThreads()) { Log.report(logvisor::Error, FMT_STRING("X doesn't support multithreading")); diff --git a/visigen/Shader.metal b/visigen/Shader.metal new file mode 100644 index 000000000..4759f48b5 --- /dev/null +++ b/visigen/Shader.metal @@ -0,0 +1,68 @@ +#include +#include + +#include "ShaderTypes.h" + +using namespace metal; + +/*static const matrix_float4x4 LookMATs[] = { + {// Forward + {1.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 1.f, 0.f}, + {0.f, -1.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 1.f}}, + {// Backward + {-1.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 1.f, 0.f}, + {0.f, 1.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 1.f}}, + {// Up + {1.f, 0.f, 0.f, 0.f}, + {0.f, -1.f, 0.f, 0.f}, + {0.f, 0.f, -1.f, 0.f}, + {0.f, 0.f, 0.f, 1.f}}, + {// Down + {1.f, 0.f, 0.f, 0.f}, + {0.f, 1.f, 0.f, 0.f}, + {0.f, 0.f, 1.f, 0.f}, + {0.f, 0.f, 0.f, 1.f}}, + {// Left + {0.f, 1.f, 0.f, 0.f}, + {0.f, 0.f, 1.f, 0.f}, + {1.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 1.f}}, + {// Right + {0.f, -1.f, 0.f, 0.f}, + {0.f, 0.f, 1.f, 0.f}, + {-1.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 1.f}}, +};*/ + +typedef struct +{ + float4 position [[position]]; + float4 color; +} ColorInOut; + +typedef struct +{ + float3 position [[attribute(VertexAttributePosition)]]; + float4 color [[attribute(VertexAttributeColor)]]; +} Vertex; + +vertex ColorInOut vertexShader(Vertex in [[stage_in]], constant Uniforms& uniforms [[buffer(BufferIndexUniforms)]]) +{ + ColorInOut out; + + float4 position = float4(in.position, 1.0); + position.y *= -1.f; + out.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * position; + out.color = in.color; + + return out; +} + +fragment float4 fragmentShader(ColorInOut in [[stage_in]]) +{ + return in.color; +} \ No newline at end of file diff --git a/visigen/ShaderTypes.h b/visigen/ShaderTypes.h new file mode 100644 index 000000000..24dce8a89 --- /dev/null +++ b/visigen/ShaderTypes.h @@ -0,0 +1,32 @@ +#ifndef ShaderTypes_h +#define ShaderTypes_h + +#ifdef __METAL_VERSION__ +#define NS_ENUM(_type, _name) \ + enum _name : _type _name; \ + enum _name : _type +#define NSInteger metal::int32_t +#else + +#import + +#endif + +#include + +typedef NS_ENUM(NSInteger, BufferIndex) { + BufferIndexVertex = 0, + BufferIndexUniforms = 1, +}; + +typedef NS_ENUM(NSInteger, VertexAttribute) { + VertexAttributePosition = 0, + VertexAttributeColor = 1, +}; + +typedef struct Uniforms { + matrix_float4x4 projectionMatrix; + matrix_float4x4 modelViewMatrix; +} Uniforms; + +#endif /* ShaderTypes_h */ diff --git a/visigen/VISIBuilder.cpp b/visigen/VISIBuilder.cpp index b9a90effe..20eb96155 100644 --- a/visigen/VISIBuilder.cpp +++ b/visigen/VISIBuilder.cpp @@ -1,6 +1,9 @@ #include "VISIBuilder.hpp" #include "logvisor/logvisor.hpp" +#include +#include + #ifndef _WIN32 #include #include @@ -15,16 +18,29 @@ VISIBuilder::PVSRenderCache::PVSRenderCache(VISIRenderer& renderer) : m_renderer static std::unique_ptr RGBABuf(new VISIRenderer::RGBA8[256 * 256 * 6]); +size_t VISIBuilder::m_frame = 0; + const VISIBuilder::Leaf& VISIBuilder::PVSRenderCache::GetLeaf(const zeus::CVector3f& vec) { auto search = m_cache.find(vec); if (search != m_cache.cend()) { - // Log.report(logvisor::Info, FMT_STRING("Cache hit")); return *search->second; } - // Log.report(logvisor::Info, FMT_STRING("Rendering")); + m_renderer.SetupRenderPass(vec); + bool needsTransparent = false; - m_renderer.RenderPVSOpaque(RGBABuf.get(), vec, needsTransparent); + m_renderer.RenderPVSOpaque(RGBABuf.get(), needsTransparent); + +// size_t outsize; +// auto* buf = VISIRenderer::makePNGBuffer(reinterpret_cast(RGBABuf.get()), 768, 512, &outsize); +// auto filename = fmt::format(FMT_STRING("outx{}.png"), m_frame++); +// std::cout << "Rendering " << filename << std::endl; +// std::ofstream fout; +// fout.open(filename, std::ios::binary | std::ios::out); +// fout.write(static_cast(buf), outsize); +// fout.close(); +// free(buf); + std::unique_ptr leafOut = std::make_unique(); for (unsigned i = 0; i < 768 * 512; ++i) { const VISIRenderer::RGBA8& pixel = RGBABuf[i]; @@ -39,18 +55,20 @@ const VISIBuilder::Leaf& VISIBuilder::PVSRenderCache::GetLeaf(const zeus::CVecto leafOut->setLightEnum(m_lightMetaBit + idx * 2, state); }; if (needsTransparent) - m_renderer.RenderPVSTransparent(setBitLambda, vec); - m_renderer.RenderPVSEntitiesAndLights(setBitLambda, setLightLambda, vec); + m_renderer.RenderPVSTransparent(setBitLambda); + m_renderer.RenderPVSEntitiesAndLights(setBitLambda, setLightLambda); return *m_cache.emplace(std::make_pair(vec, std::move(leafOut))).first->second; } void VISIBuilder::Progress::report(int divisions) { m_prog += 1.f / divisions; - // printf(" %g%% \r", m_prog * 100.f); - // fflush(stdout); - if (m_updatePercent) + if (m_updatePercent != nullptr) { m_updatePercent(m_prog); + } else { + printf(" %g%% \r", m_prog * 100.f); + fflush(stdout); + } } void VISIBuilder::Node::buildChildren(int level, int divisions, const zeus::CAABox& curAabb, PVSRenderCache& rc, diff --git a/visigen/VISIBuilder.hpp b/visigen/VISIBuilder.hpp index ca1dbdb79..c9ed7e23e 100644 --- a/visigen/VISIBuilder.hpp +++ b/visigen/VISIBuilder.hpp @@ -1,9 +1,9 @@ #pragma once #include "VISIRenderer.hpp" -#include "zeus/CAABox.hpp" -#include "xxhash/xxhash.h" #include "athena/MemoryWriter.hpp" +#include "xxhash/xxhash.h" +#include "zeus/CAABox.hpp" #include #ifdef _WIN32 @@ -23,6 +23,8 @@ struct hash { } // namespace std struct VISIBuilder { + static size_t m_frame; + struct Leaf { std::vector bits; void setBit(size_t bit) { diff --git a/visigen/VISIRenderer.cpp b/visigen/VISIRenderer.cpp index a95463e27..9db4033bb 100644 --- a/visigen/VISIRenderer.cpp +++ b/visigen/VISIRenderer.cpp @@ -1,112 +1,144 @@ #include "VISIRenderer.hpp" -#include "athena/FileReader.hpp" -#include "zeus/CAABox.hpp" #include "VISIBuilder.hpp" -#include "zeus/CFrustum.hpp" +#include "athena/FileReader.hpp" #include "logvisor/logvisor.hpp" +#include + static logvisor::Module Log("visigen"); -static const char* VS = - "#version 330\n" - "layout(location=0) in vec4 posIn;\n" - "layout(location=1) in vec4 colorIn;\n" - "\n" - "uniform UniformBlock\n" - "{\n" - " mat4 xf;\n" - "};\n" - "\n" - "struct VertToFrag\n" - "{\n" - " vec4 color;\n" - "};\n" - "\n" - "out VertToFrag vtf;\n" - "void main()\n" - "{\n" - " vtf.color = colorIn;\n" - " gl_Position = xf * vec4(posIn.xyz, 1.0);\n" - "}\n"; +/* structure to store PNG image bytes */ +struct mem_encode +{ + char *buffer; + size_t size; +}; -static const char* FS = - "#version 330\n" - "struct VertToFrag\n" - "{\n" - " vec4 color;\n" - "};\n" - "\n" - "in VertToFrag vtf;\n" - "layout(location=0) out vec4 colorOut;\n" - "void main()\n" - "{\n" - " colorOut = vtf.color;\n" - "}\n"; +static void +my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + /* with libpng15 next line causes pointer deference error; use libpng12 */ + struct mem_encode* p=(struct mem_encode*)png_get_io_ptr(png_ptr); /* was png_ptr->io_ptr */ + size_t nsize = p->size + length; -static const uint32_t AABBIdxs[20] = {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 1, 7, 3, 5, 5, 0, 0, 2, 6, 4}; + /* allocate or grow buffer */ + if(p->buffer) + p->buffer = (char *) realloc(p->buffer, nsize); + else + p->buffer = (char *) malloc(nsize); -bool VISIRenderer::SetupShaders() { - m_vtxShader = glCreateShader(GL_VERTEX_SHADER); - m_fragShader = glCreateShader(GL_FRAGMENT_SHADER); - m_program = glCreateProgram(); + if(!p->buffer) + png_error(png_ptr, "Write Error"); - glShaderSource(m_vtxShader, 1, &VS, nullptr); - glCompileShader(m_vtxShader); - GLint status; - glGetShaderiv(m_vtxShader, GL_COMPILE_STATUS, &status); - if (status != GL_TRUE) { - GLint logLen; - glGetShaderiv(m_vtxShader, GL_INFO_LOG_LENGTH, &logLen); - char* log = (char*)malloc(logLen); - glGetShaderInfoLog(m_vtxShader, logLen, nullptr, log); - Log.report(logvisor::Error, FMT_STRING("unable to compile vert source\n{}\n{}\n"), log, VS); - free(log); - return false; + /* copy new bytes to end of buffer */ + memcpy(p->buffer + p->size, data, length); + p->size += length; +} + +/* + write an rgba image to a memory buffer in PNG format, without any fanciness. + + Params: rgba - the rgba values + width - image width + height - image height + outsize - return for size of output buffer + Returns: pointer to allocated buffer holding png data +*/ +void *VISIRenderer::makePNGBuffer(unsigned char *rgba, int width, int height, size_t *outsize) +{ + int code = 0; + // FILE *fp; + png_structp png_ptr = 0; + png_infop info_ptr =0; + png_bytep row = 0; + + struct mem_encode state = {0, 0}; + + *outsize = 0; + + // Initialize write structure + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) { + fprintf(stderr, "Could not allocate write struct\n"); + code = 1; + goto finalise; } - glShaderSource(m_fragShader, 1, &FS, nullptr); - glCompileShader(m_fragShader); - glGetShaderiv(m_fragShader, GL_COMPILE_STATUS, &status); - if (status != GL_TRUE) { - GLint logLen; - glGetShaderiv(m_fragShader, GL_INFO_LOG_LENGTH, &logLen); - char* log = (char*)malloc(logLen); - glGetShaderInfoLog(m_fragShader, logLen, nullptr, log); - Log.report(logvisor::Error, FMT_STRING("unable to compile frag source\n{}\n{}\n"), log, FS); - free(log); - return false; +// Initialize info structure + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + fprintf(stderr, "Could not allocate info struct\n"); + code = 1; + goto finalise; } - glAttachShader(m_program, m_vtxShader); - glAttachShader(m_program, m_fragShader); - - glLinkProgram(m_program); - glGetProgramiv(m_program, GL_LINK_STATUS, &status); - if (status != GL_TRUE) { - GLint logLen; - glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &logLen); - char* log = (char*)malloc(logLen); - glGetProgramInfoLog(m_program, logLen, nullptr, log); - Log.report(logvisor::Error, FMT_STRING("unable to link shader program\n{}\n"), log); - free(log); - return false; + // Setup Exception handling + if (setjmp(png_jmpbuf(png_ptr))) { + fprintf(stderr, "Error during png creation\n"); + code = 1; + goto finalise; } - glUseProgram(m_program); - m_uniLoc = glGetUniformBlockIndex(m_program, "UniformBlock"); + // png_init_io(png_ptr, fp); - glGenBuffers(1, &m_uniformBufferGL); - glBindBuffer(GL_UNIFORM_BUFFER, m_uniformBufferGL); - glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformBuffer), nullptr, GL_DYNAMIC_DRAW); + /* if my_png_flush() is not needed, change the arg to NULL */ + png_set_write_fn(png_ptr, &state, my_png_write_data, NULL); - glGenBuffers(1, &m_aabbIBO); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_aabbIBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, 20 * 4, AABBIdxs, GL_STATIC_DRAW); + // Write header (8 bit colour depth) + png_set_IHDR(png_ptr, info_ptr, width, height, + 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex(0xffffffff); + // Set title + /* + if (title != NULL) { + png_text title_text; + title_text.compression = PNG_TEXT_COMPRESSION_NONE; + title_text.key = "Title"; + title_text.text = title; + png_set_text(png_ptr, info_ptr, &title_text, 1); + } + */ - return true; + png_write_info(png_ptr, info_ptr); + + // Allocate memory for one row (4 bytes per pixel - RGBA) + row = (png_bytep) malloc(4 * width * sizeof(png_byte)); + + // Write image data + int x, y; + for (y=0 ; y> 8) & 0xff) / 255.f, ((i >> 16) & 0xff) / 255.f, 1.f); } std::vector VISIRenderer::AABBToVerts(const zeus::CAABox& aabb, const zeus::CColor& color) { @@ -128,302 +160,8 @@ std::vector VISIRenderer::AABBToVerts(const zeus::CAA return verts; } -static zeus::CColor ColorForIndex(int i) { - i += 1; - return zeus::CColor((i & 0xff) / 255.f, ((i >> 8) & 0xff) / 255.f, ((i >> 16) & 0xff) / 255.f, 1.f); -} - -bool VISIRenderer::SetupVertexBuffersAndFormats() { - for (Model& model : m_models) { - glGenVertexArrays(1, &model.vao); - glGenBuffers(1, &model.vbo); - glGenBuffers(1, &model.ibo); - - glBindVertexArray(model.vao); - - glBindBuffer(GL_ARRAY_BUFFER, model.vbo); - glBufferData(GL_ARRAY_BUFFER, model.verts.size() * sizeof(Model::Vert), model.verts.data(), GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), 0); - glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), (void*)16); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, model.ibo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, model.idxs.size() * 4, model.idxs.data(), GL_STATIC_DRAW); - } - - int idx = m_models.size(); - for (Entity& ent : m_entities) { - glGenVertexArrays(1, &ent.vao); - glGenBuffers(1, &ent.vbo); - - glBindVertexArray(ent.vao); - - auto verts = AABBToVerts(ent.aabb, ColorForIndex(idx++)); - glBindBuffer(GL_ARRAY_BUFFER, ent.vbo); - glBufferData(GL_ARRAY_BUFFER, verts.size() * sizeof(Model::Vert), verts.data(), GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), 0); - glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), (void*)16); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_aabbIBO); - } - - for (Light& light : m_lights) { - glGenVertexArrays(1, &light.vao); - glGenBuffers(1, &light.vbo); - - glBindVertexArray(light.vao); - - Model::Vert vert; - vert.pos = light.point; - vert.color = ColorForIndex(idx++); - glBindBuffer(GL_ARRAY_BUFFER, light.vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(Model::Vert), &vert, GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), 0); - glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), (void*)16); - } - - m_queryCount = m_models.size() + m_entities.size() + m_lights.size(); - m_queries.reset(new GLuint[m_queryCount]); - m_queryBools.reset(new bool[m_queryCount]); - glGenQueries(GLsizei(m_queryCount), m_queries.get()); - - return true; -} - -static zeus::CMatrix4f g_Proj; - -static void CalculateProjMatrix() { - float znear = 0.2f; - float zfar = 1000.f; - float tfov = std::tan(zeus::degToRad(90.f * 0.5f)); - float top = znear * tfov; - float bottom = -top; - float right = znear * tfov; - float left = -right; - - float rml = right - left; - float rpl = right + left; - float tmb = top - bottom; - float tpb = top + bottom; - float fpn = zfar + znear; - float fmn = zfar - znear; - - g_Proj = zeus::CMatrix4f(2.f * znear / rml, 0.f, rpl / rml, 0.f, 0.f, 2.f * znear / tmb, tpb / tmb, 0.f, 0.f, 0.f, - -fpn / fmn, -2.f * zfar * znear / fmn, 0.f, 0.f, -1.f, 0.f); -} - -static const zeus::CMatrix4f LookMATs[] = { - {// Forward - 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, - {// Backward - -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, - {// Up - 1.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 1.f}, - {// Down - 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f}, - {// Left - 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, - {// Right - 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, -}; - -void VISIRenderer::RenderPVSOpaque(RGBA8* bufOut, const zeus::CVector3f& pos, bool& needTransparent) { - glViewport(0, 0, 768, 512); - glEnable(GL_CULL_FACE); - glDepthMask(GL_TRUE); - glDepthFunc(GL_LEQUAL); - glEnable(GL_DEPTH_TEST); - glClearColor(0.f, 0.f, 0.f, 1.f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - for (int j = 0; j < 6; ++j) { - GLint x = (j % 3) * 256; - GLint y = (j / 3) * 256; - glViewport(x, y, 256, 256); - - zeus::CMatrix4f mv = LookMATs[j] * zeus::CTransform::Translate(-pos).toMatrix4f(); - m_uniformBuffer.m_xf = g_Proj * mv; - glBindBuffer(GL_UNIFORM_BUFFER, m_uniformBufferGL); - glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformBuffer), &m_uniformBuffer, GL_DYNAMIC_DRAW); - - glUniformBlockBinding(m_program, m_uniLoc, 0); - glBindBufferRange(GL_UNIFORM_BUFFER, 0, m_uniformBufferGL, 0, sizeof(UniformBuffer)); - - zeus::CFrustum frustum; - frustum.updatePlanes(mv, g_Proj); - - // Draw frontfaces - glCullFace(GL_BACK); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - for (const Model& model : m_models) { - if (!frustum.aabbFrustumTest(model.aabb)) - continue; - glBindVertexArray(model.vao); - for (const Model::Surface& surf : model.surfaces) { - // Non-transparents first - if (!surf.transparent) - glDrawElements(model.topology, surf.count, GL_UNSIGNED_INT, - reinterpret_cast(uintptr_t(surf.first * 4))); - } - } - } - - // m_swapFunc(); - glFinish(); - glReadPixels(0, 0, 768, 512, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)bufOut); -} - -void VISIRenderer::RenderPVSTransparent(const std::function& passFunc, const zeus::CVector3f& pos) { - glDepthMask(GL_FALSE); - - for (int j = 0; j < 6; ++j) { - GLint x = (j % 3) * 256; - GLint y = (j / 3) * 256; - glViewport(x, y, 256, 256); - - zeus::CMatrix4f mv = LookMATs[j] * zeus::CTransform::Translate(-pos).toMatrix4f(); - m_uniformBuffer.m_xf = g_Proj * mv; - glBindBuffer(GL_UNIFORM_BUFFER, m_uniformBufferGL); - glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformBuffer), &m_uniformBuffer, GL_DYNAMIC_DRAW); - - glUniformBlockBinding(m_program, m_uniLoc, 0); - glBindBufferRange(GL_UNIFORM_BUFFER, 0, m_uniformBufferGL, 0, sizeof(UniformBuffer)); - - zeus::CFrustum frustum; - frustum.updatePlanes(mv, g_Proj); - - memset(m_queryBools.get(), 0, m_queryCount); - - int idx = 0; - for (const Model& model : m_models) { - if (!frustum.aabbFrustumTest(model.aabb)) { - ++idx; - continue; - } - glBindVertexArray(model.vao); - glBeginQuery(GL_ANY_SAMPLES_PASSED_CONSERVATIVE, m_queries[idx]); - m_queryBools[idx] = true; - for (const Model::Surface& surf : model.surfaces) { - // transparents - if (surf.transparent) - glDrawElements(model.topology, surf.count, GL_UNSIGNED_INT, - reinterpret_cast(uintptr_t(surf.first * 4))); - } - glEndQuery(GL_ANY_SAMPLES_PASSED_CONSERVATIVE); - ++idx; - } - - for (int i = 0; i < idx; ++i) { - if (m_queryBools[i]) { - GLint res; - glGetQueryObjectiv(m_queries[i], GL_QUERY_RESULT, &res); - if (res) - passFunc(i); - } - } - } -} - -void VISIRenderer::RenderPVSEntitiesAndLights(const std::function& passFunc, - const std::function& lightPassFunc, - const zeus::CVector3f& pos) { - glDepthMask(GL_FALSE); - - for (int j = 0; j < 6; ++j) { - GLint x = (j % 3) * 256; - GLint y = (j / 3) * 256; - glViewport(x, y, 256, 256); - - zeus::CMatrix4f mv = LookMATs[j] * zeus::CTransform::Translate(-pos).toMatrix4f(); - m_uniformBuffer.m_xf = g_Proj * mv; - glBindBuffer(GL_UNIFORM_BUFFER, m_uniformBufferGL); - glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformBuffer), &m_uniformBuffer, GL_DYNAMIC_DRAW); - - glUniformBlockBinding(m_program, m_uniLoc, 0); - glBindBufferRange(GL_UNIFORM_BUFFER, 0, m_uniformBufferGL, 0, sizeof(UniformBuffer)); - - zeus::CFrustum frustum; - frustum.updatePlanes(mv, g_Proj); - - memset(m_queryBools.get(), 0, m_queryCount); - - int idx = m_models.size(); - for (const Entity& ent : m_entities) { - if (!frustum.aabbFrustumTest(ent.aabb)) { - ++idx; - continue; - } - glBindVertexArray(ent.vao); - m_queryBools[idx] = true; - glBeginQuery(GL_ANY_SAMPLES_PASSED_CONSERVATIVE, m_queries[idx]); - glDrawElements(GL_TRIANGLE_STRIP, 20, GL_UNSIGNED_INT, 0); - glEndQuery(GL_ANY_SAMPLES_PASSED_CONSERVATIVE); - ++idx; - } - - for (const Light& light : m_lights) { - if (!frustum.pointFrustumTest(light.point)) { - ++idx; - continue; - } - glBindVertexArray(light.vao); - m_queryBools[idx] = true; - glBeginQuery(GL_ANY_SAMPLES_PASSED_CONSERVATIVE, m_queries[idx]); - glDrawArrays(GL_POINTS, 0, 1); - glEndQuery(GL_ANY_SAMPLES_PASSED_CONSERVATIVE); - ++idx; - } - - idx = m_models.size(); - for (const Entity& ent : m_entities) { - (void)ent; - if (m_queryBools[idx]) { - GLint res; - glGetQueryObjectiv(m_queries[idx], GL_QUERY_RESULT, &res); - if (res) - passFunc(idx); - } - ++idx; - } - - int lightIdx = 0; - for (const Light& light : m_lights) { - if (m_queryBools[idx]) { - GLint res; - glGetQueryObjectiv(m_queries[idx], GL_QUERY_RESULT, &res); - EPVSVisSetState state = - m_totalAABB.pointInside(light.point) ? EPVSVisSetState::EndOfTree : EPVSVisSetState::OutOfBounds; - if (res && state == EPVSVisSetState::EndOfTree) - state = EPVSVisSetState::NodeFound; - lightPassFunc(lightIdx, state); - } - ++lightIdx; - ++idx; - } - } -} - void VISIRenderer::Run(FPercent updatePercent) { m_updatePercent = updatePercent; - CalculateProjMatrix(); - - if (glewInit() != GLEW_OK) { - Log.report(logvisor::Error, FMT_STRING("unable to init glew")); - m_return = 1; - return; - } - - if (!GLEW_ARB_occlusion_query2) { - Log.report(logvisor::Error, FMT_STRING("GL_ARB_occlusion_query2 extension not present")); - m_return = 1; - return; - } if (!SetupShaders()) { m_return = 1; @@ -456,7 +194,7 @@ void VISIRenderer::Run(FPercent updatePercent) { zeus::CColor color = ColorForIndex(i); Model& model = m_models[i]; uint32_t topology = r.readUint32Big(); - model.topology = topology ? GL_TRIANGLE_STRIP : GL_TRIANGLES; + model.topology = static_cast(topology); uint32_t vertCount = r.readUint32Big(); model.verts.reserve(vertCount); diff --git a/visigen/VISIRenderer.hpp b/visigen/VISIRenderer.hpp index 08d1bbd3f..948eb3266 100644 --- a/visigen/VISIRenderer.hpp +++ b/visigen/VISIRenderer.hpp @@ -1,7 +1,7 @@ #pragma once -#include "boo/graphicsdev/glew.h" #include "hecl/SystemChar.hpp" +#include "hecl/HMDLMeta.hpp" #include "zeus/CColor.hpp" #include "zeus/CMatrix4f.hpp" #include "zeus/CAABox.hpp" @@ -13,18 +13,9 @@ enum class EPVSVisSetState { EndOfTree, NodeFound, OutOfBounds }; class VISIRenderer { friend struct VISIBuilder; - int m_argc; - const hecl::SystemChar** m_argv; - int m_return = 0; - - zeus::CAABox m_totalAABB; - - struct UniformBuffer { - zeus::CMatrix4f m_xf; - } m_uniformBuffer; - +public: struct Model { - GLenum topology; + hecl::HMDLTopology topology; zeus::CAABox aabb; struct Vert { @@ -34,7 +25,6 @@ class VISIRenderer { std::vector verts; std::vector idxs; - GLuint vbo, ibo, vao; struct Surface { uint32_t first; @@ -47,32 +37,29 @@ class VISIRenderer { struct Entity { uint32_t entityId; zeus::CAABox aabb; - GLuint vbo, vao; }; struct Light { zeus::CVector3f point; - GLuint vbo, vao; }; - GLuint m_vtxShader, m_fragShader, m_program, m_uniLoc; - GLuint m_uniformBufferGL; - GLuint m_aabbIBO; - bool SetupShaders(); +protected: + int m_argc; + const hecl::SystemChar** m_argv; + int m_return = 0; + + zeus::CAABox m_totalAABB; + + virtual bool SetupShaders() = 0; std::vector m_models; std::vector m_entities; std::vector m_lights; - bool SetupVertexBuffersAndFormats(); - - size_t m_queryCount; - std::unique_ptr m_queries; - std::unique_ptr m_queryBools; + virtual bool SetupVertexBuffersAndFormats() = 0; + virtual void SetupRenderPass(const zeus::CVector3f& pos) = 0; FPercent m_updatePercent; - static std::vector AABBToVerts(const zeus::CAABox& aabb, const zeus::CColor& color); - public: bool m_terminate = false; struct RGBA8 { @@ -85,10 +72,13 @@ public: VISIRenderer(int argc, const hecl::SystemChar** argv) : m_argc(argc), m_argv(argv) {} void Run(FPercent updatePercent); void Terminate(); - void RenderPVSOpaque(RGBA8* bufOut, const zeus::CVector3f& pos, bool& needTransparent); - void RenderPVSTransparent(const std::function& passFunc, const zeus::CVector3f& pos); - void RenderPVSEntitiesAndLights(const std::function& passFunc, - const std::function& lightPassFunc, - const zeus::CVector3f& pos); + virtual void RenderPVSOpaque(RGBA8* bufOut, bool& needTransparent) = 0; + virtual void RenderPVSTransparent(const std::function& passFunc) = 0; + virtual void RenderPVSEntitiesAndLights(const std::function& passFunc, + const std::function& lightPassFunc) = 0; int ReturnVal() const { return m_return; } + + static std::vector AABBToVerts(const zeus::CAABox& aabb, const zeus::CColor& color); + static zeus::CColor ColorForIndex(uint32_t i); + static void* makePNGBuffer(unsigned char* rgba, int width, int height, size_t* outsize); }; diff --git a/visigen/VISIRendererMetal.hh b/visigen/VISIRendererMetal.hh new file mode 100644 index 000000000..bbdeb2b94 --- /dev/null +++ b/visigen/VISIRendererMetal.hh @@ -0,0 +1,26 @@ +#pragma once + +#include "VISIRenderer.hpp" +#import +#import +#import + +@interface MetalRenderer : NSObject +@end + +class VISIRendererMetal : public VISIRenderer { + MetalRenderer* view; + + bool SetupShaders() override; + bool SetupVertexBuffersAndFormats() override; + void SetupRenderPass(const zeus::CVector3f& pos) override; + +public: + VISIRendererMetal(int argc, const hecl::SystemChar** argv) : VISIRenderer(argc, argv) { + view = [[MetalRenderer alloc] init]; + } + void RenderPVSOpaque(RGBA8* out, bool& needTransparent) override; + void RenderPVSTransparent(const std::function& passFunc) override; + void RenderPVSEntitiesAndLights(const std::function& passFunc, + const std::function& lightPassFunc) override; +}; \ No newline at end of file diff --git a/visigen/VISIRendererMetal.mm b/visigen/VISIRendererMetal.mm new file mode 100644 index 000000000..66927c12e --- /dev/null +++ b/visigen/VISIRendererMetal.mm @@ -0,0 +1,480 @@ +#include "VISIRendererMetal.hh" +#include "ShaderTypes.h" + +#include + +static zeus::CMatrix4f g_Proj; + +constexpr zeus::CMatrix4f VulkanCorrect(1.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 0.5f, 0.5f + FLT_EPSILON, + 0.f, 0.f, 0.f, 1.f); + +static void CalculateProjMatrix() { + float znear = 0.2f; + float zfar = 1000.f; + float tfov = std::tan(zeus::degToRad(90.f * 0.5f)); + float top = znear * tfov; + float bottom = -top; + float right = znear * tfov; + float left = -right; + + float rml = right - left; + float rpl = right + left; + float tmb = top - bottom; + float tpb = top + bottom; + float fpn = zfar + znear; + float fmn = zfar - znear; + + zeus::CMatrix4f mat2{ + 2.f * znear / rml, 0.f, rpl / rml, 0.f, 0.f, 2.f * znear / tmb, tpb / tmb, 0.f, 0.f, 0.f, -fpn / fmn, + -2.f * zfar * znear / fmn, 0.f, 0.f, -1.f, 0.f}; + g_Proj = VulkanCorrect * mat2; +} + +static constexpr std::array AABBIdxs{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 1, 7, 3, 5, 5, 0, 0, 2, 6, 4}; + +static const zeus::CMatrix4f LookMATs[] = { + {// Forward + 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Backward + -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Up + 1.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Down + 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Left + 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Right + 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, +}; + +using Vertex = VISIRenderer::Model::Vert; + +@implementation MetalRenderer { + MTLPixelFormat _pixelFormat; + MTLPixelFormat _depthPixelFormat; + + id _device; + id _library; + id _commandQueue; + id _pipelineState; + id _depthState; + id _depthStateNoWrite; + dispatch_semaphore_t _semaphore; + id _uniformBuffer; + id _vertexBuffer; + id _indexBuffer; + id _modelQueryBuffer; + id _entityLightQueryBuffer; + id _aabbIndexBuffer; + id _renderTexture; + id _depthTexture; + + std::array _frustums; + NSUInteger _entityVertStart; +} + +- (bool)setup { + CalculateProjMatrix(); + + // Create device. + _device = MTLCreateSystemDefaultDevice(); + + // Set view settings. + _pixelFormat = MTLPixelFormatRGBA8Unorm; + _depthPixelFormat = MTLPixelFormatDepth32Float_Stencil8; + + // Load shaders. + NSError *error = nil; + _library = [_device newLibraryWithFile:@"Shader.metallib" error:&error]; + if (_library == nullptr) { + NSLog(@"Failed to load library. error %@", error); + return false; + } + id vertFunc = [_library newFunctionWithName:@"vertexShader"]; + id fragFunc = [_library newFunctionWithName:@"fragmentShader"]; + + // Create render texture. + MTLTextureDescriptor *renderTexDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:_pixelFormat + width:768 + height:512 + mipmapped:NO]; + renderTexDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead; + _renderTexture = [_device newTextureWithDescriptor:renderTexDesc]; + + // Create depth texture. + MTLTextureDescriptor *depthTexDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:_depthPixelFormat + width:768 + height:512 + mipmapped:NO]; + depthTexDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead; + _depthTexture = [_device newTextureWithDescriptor:depthTexDesc]; + + // Create depth state. + MTLDepthStencilDescriptor *depthDesc = [[MTLDepthStencilDescriptor alloc] init]; + depthDesc.depthCompareFunction = MTLCompareFunctionLessEqual; + depthDesc.depthWriteEnabled = YES; + _depthState = [_device newDepthStencilStateWithDescriptor:depthDesc]; + + // Create depth state (no write). + MTLDepthStencilDescriptor *depthDescNoWrite = [[MTLDepthStencilDescriptor alloc] init]; + depthDescNoWrite.depthCompareFunction = MTLCompareFunctionLessEqual; + depthDescNoWrite.depthWriteEnabled = NO; + _depthStateNoWrite = [_device newDepthStencilStateWithDescriptor:depthDescNoWrite]; + + // Create vertex descriptor. + MTLVertexDescriptor *vertDesc = [[MTLVertexDescriptor alloc] init]; + vertDesc.attributes[VertexAttributePosition].format = MTLVertexFormatFloat3; + vertDesc.attributes[VertexAttributePosition].offset = offsetof(Vertex, pos); + vertDesc.attributes[VertexAttributePosition].bufferIndex = BufferIndexVertex; + vertDesc.attributes[VertexAttributeColor].format = MTLVertexFormatFloat4; + vertDesc.attributes[VertexAttributeColor].offset = offsetof(Vertex, color); + vertDesc.attributes[VertexAttributeColor].bufferIndex = BufferIndexVertex; + vertDesc.layouts[BufferIndexVertex].stride = sizeof(Vertex); + vertDesc.layouts[BufferIndexVertex].stepRate = 1; + vertDesc.layouts[BufferIndexVertex].stepFunction = MTLVertexStepFunctionPerVertex; + + // Create pipeline state. + MTLRenderPipelineDescriptor *pipelineDesc = [[MTLRenderPipelineDescriptor alloc] init]; + pipelineDesc.rasterSampleCount = 1; + pipelineDesc.vertexFunction = vertFunc; + pipelineDesc.fragmentFunction = fragFunc; + pipelineDesc.vertexDescriptor = vertDesc; + pipelineDesc.colorAttachments[0].pixelFormat = _pixelFormat; + pipelineDesc.depthAttachmentPixelFormat = _depthPixelFormat; + _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineDesc error:&error]; + if (_pipelineState == nullptr) { + NSLog(@"Failed to create pipeline state, error %@", error); + return false; + } + return true; +} + +- (bool)setupModels:(std::vector &)models + entities:(std::vector &)entities + lights:(std::vector &)lights { + NSUInteger vertCount = 0; + NSUInteger indexCount = 0; + for (const auto &model : models) { + vertCount += model.verts.size(); + indexCount += model.idxs.size(); + } + _entityVertStart = vertCount; + vertCount += 8 * entities.size(); + vertCount += lights.size(); + _vertexBuffer = [_device newBufferWithLength:vertCount * sizeof(Vertex) options:MTLResourceStorageModeManaged]; + _indexBuffer = [_device newBufferWithLength:indexCount * sizeof(uint32_t) options:MTLResourceStorageModeManaged]; + _modelQueryBuffer = [_device newBufferWithLength:models.size() * 6 * sizeof(uint64_t) + options:MTLResourceStorageModeManaged]; + _entityLightQueryBuffer = [_device newBufferWithLength:(entities.size() + lights.size()) * 6 * sizeof(uint64_t) + options:MTLResourceStorageModeManaged]; + + auto *buffer = static_cast([_vertexBuffer contents]); + auto *indexBuffer = static_cast([_indexBuffer contents]); + for (const auto &model : models) { + memcpy(buffer, model.verts.data(), model.verts.size() * sizeof(Vertex)); + memcpy(indexBuffer, model.idxs.data(), model.idxs.size() * sizeof(uint32_t)); + buffer += model.verts.size(); + indexBuffer += model.idxs.size(); + } + auto idx = static_cast(models.size()); + for (const auto &ent : entities) { + auto verts = VISIRenderer::AABBToVerts(ent.aabb, VISIRenderer::ColorForIndex(idx++)); + memcpy(buffer, verts.data(), verts.size() * sizeof(Vertex)); + buffer += verts.size(); + } + for (const auto &light : lights) { + auto *vert = buffer++; + vert->pos = light.point; + vert->color = VISIRenderer::ColorForIndex(idx++); + } + [_vertexBuffer didModifyRange:NSMakeRange(0, [_vertexBuffer length])]; + [_indexBuffer didModifyRange:NSMakeRange(0, [_indexBuffer length])]; + + _uniformBuffer = [_device newBufferWithLength:sizeof(Uniforms) * 6 options:MTLResourceStorageModeManaged]; + _aabbIndexBuffer = [_device newBufferWithBytes:AABBIdxs.data() + length:AABBIdxs.size() * sizeof(uint16_t) + options:MTLResourceStorageModeManaged]; + + _semaphore = dispatch_semaphore_create(0); + _commandQueue = [_device newCommandQueue]; + return true; +} + +- (void)setupRenderPass:(const zeus::CVector3f &)pos { + auto posMat = zeus::CTransform::Translate(-pos).toMatrix4f(); + auto *buffer = static_cast([_uniformBuffer contents]); + for (uint16_t j = 0; j < 6; ++j) { + static_assert(sizeof(zeus::CMatrix4f) == sizeof(matrix_float4x4)); + zeus::CMatrix4f modelView = LookMATs[j] * posMat; + _frustums[j].updatePlanes(modelView, g_Proj); + memcpy(&buffer->projectionMatrix, &g_Proj, sizeof(matrix_float4x4)); + memcpy(&buffer->modelViewMatrix, &modelView, sizeof(matrix_float4x4)); + buffer++; + } + [_uniformBuffer didModifyRange:NSMakeRange(0, [_uniformBuffer length])]; +} + +- (void)renderPVSOpaque:(std::vector &)models + out:(VISIRenderer::RGBA8 *)out + needTransparent:(bool &)needTransparent { + id commandBuffer = [_commandQueue commandBuffer]; + + __block dispatch_semaphore_t semaphore = _semaphore; + [commandBuffer addCompletedHandler:^(id buffer) { + dispatch_semaphore_signal(semaphore); + }]; + + MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; + passDescriptor.colorAttachments[0].texture = _renderTexture; + passDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; + passDescriptor.depthAttachment.texture = _depthTexture; + passDescriptor.depthAttachment.storeAction = MTLStoreActionStore; // stored for following render passes + id encoder = [commandBuffer renderCommandEncoderWithDescriptor:passDescriptor]; + + [encoder setDepthStencilState:_depthState]; + [encoder setRenderPipelineState:_pipelineState]; + [encoder setCullMode:MTLCullModeBack]; + [encoder setVertexBuffer:_vertexBuffer offset:0 atIndex:BufferIndexVertex]; + [encoder setVertexBuffer:_uniformBuffer offset:0 atIndex:BufferIndexUniforms]; + + for (int j = 0; j < 6; ++j) { + GLint x = (j % 3) * 256; + GLint y = (j / 3) * 256; + [encoder setViewport:{x, y, 256, 256, 0, 1}]; + if (j > 0) { + [encoder setVertexBufferOffset:j * sizeof(Uniforms) atIndex:BufferIndexUniforms]; + } + NSUInteger vertexBufferOffset = 0; + NSUInteger indexBufferOffset = 0; + for (const auto &model : models) { + if (_frustums[j].aabbFrustumTest(model.aabb)) { + [encoder setVertexBufferOffset:vertexBufferOffset atIndex:BufferIndexVertex]; + for (const auto &surf : model.surfaces) { + // Non-transparents first + if (surf.transparent) { + needTransparent = true; + } else { + MTLPrimitiveType type = model.topology == hecl::HMDLTopology::TriStrips ? MTLPrimitiveTypeTriangleStrip + : MTLPrimitiveTypeTriangle; + [encoder drawIndexedPrimitives:type + indexCount:surf.count + indexType:MTLIndexTypeUInt32 + indexBuffer:_indexBuffer + indexBufferOffset:indexBufferOffset + surf.first * sizeof(uint32_t)]; + } + } + } + vertexBufferOffset += model.verts.size() * sizeof(Vertex); + indexBufferOffset += model.idxs.size() * sizeof(uint32_t); + } + } + + [encoder endEncoding]; + [commandBuffer commit]; + + dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER); + + [_renderTexture getBytes:out + bytesPerRow:sizeof(VISIRenderer::RGBA8) * _renderTexture.width + fromRegion:MTLRegionMake2D(0, 0, _renderTexture.width, _renderTexture.height) + mipmapLevel:0]; +} + +- (void)renderPVSTransparent:(std::vector &)models + passFunc:(const std::function &)passFunc { + // Zero out query buffer + memset([_modelQueryBuffer contents], 0, [_modelQueryBuffer length]); + [_modelQueryBuffer didModifyRange:NSMakeRange(0, [_modelQueryBuffer length])]; + + id commandBuffer = [_commandQueue commandBuffer]; + + __block dispatch_semaphore_t semaphore = _semaphore; + [commandBuffer addCompletedHandler:^(id buffer) { + dispatch_semaphore_signal(semaphore); + }]; + + MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; + passDescriptor.colorAttachments[0].texture = _renderTexture; + passDescriptor.colorAttachments[0].storeAction = MTLStoreActionDontCare; // no longer care about the render texture + passDescriptor.depthAttachment.texture = _depthTexture; + passDescriptor.depthAttachment.loadAction = MTLLoadActionLoad; + passDescriptor.depthAttachment.storeAction = MTLStoreActionStore; // still stored for following render passes + passDescriptor.visibilityResultBuffer = _modelQueryBuffer; + id encoder = [commandBuffer renderCommandEncoderWithDescriptor:passDescriptor]; + + [encoder setDepthStencilState:_depthStateNoWrite]; + [encoder setRenderPipelineState:_pipelineState]; + [encoder setCullMode:MTLCullModeBack]; + [encoder setVertexBuffer:_vertexBuffer offset:0 atIndex:BufferIndexVertex]; + [encoder setVertexBuffer:_uniformBuffer offset:0 atIndex:BufferIndexUniforms]; + + NSUInteger queryCount = 0; + for (int j = 0; j < 6; ++j) { + GLint x = (j % 3) * 256; + GLint y = (j / 3) * 256; + [encoder setViewport:{x, y, 256, 256, 0, 1}]; + if (j > 0) { + [encoder setVertexBufferOffset:j * sizeof(Uniforms) atIndex:BufferIndexUniforms]; + } + NSUInteger vertexBufferOffset = 0; + NSUInteger indexBufferOffset = 0; + for (const auto &model : models) { + if (_frustums[j].aabbFrustumTest(model.aabb)) { + [encoder setVertexBufferOffset:vertexBufferOffset atIndex:BufferIndexVertex]; + [encoder setVisibilityResultMode:MTLVisibilityResultModeBoolean offset:queryCount * sizeof(uint64_t)]; + for (const auto &surf : model.surfaces) { + // Only transparent surfaces + if (surf.transparent) { + MTLPrimitiveType type = model.topology == hecl::HMDLTopology::TriStrips ? MTLPrimitiveTypeTriangleStrip + : MTLPrimitiveTypeTriangle; + [encoder drawIndexedPrimitives:type + indexCount:surf.count + indexType:MTLIndexTypeUInt32 + indexBuffer:_indexBuffer + indexBufferOffset:indexBufferOffset + surf.first * sizeof(uint32_t)]; + } + } + } + vertexBufferOffset += model.verts.size() * sizeof(Vertex); + indexBufferOffset += model.idxs.size() * sizeof(uint32_t); + ++queryCount; + } + } + + [encoder endEncoding]; + [commandBuffer commit]; + + dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER); + + auto *queries = static_cast([_modelQueryBuffer contents]); + for (int i = 0; i < models.size(); ++i) { + for (int j = 0; j < 6; ++j) { + if (queries[i + j * models.size()] != 0u) { + passFunc(i); + break; + } + } + } +} + +- (void)renderPVSEntities:(std::vector &)entities + entityPassFunc:(const std::function &)entityPassFunc + lights:(std::vector &)lights + lightPassFunc:(const std::function &)lightPassFunc + totalAABB:(const zeus::CAABox &)totalAABB { + // Zero out query buffer + memset([_entityLightQueryBuffer contents], 0, [_entityLightQueryBuffer length]); + [_entityLightQueryBuffer didModifyRange:NSMakeRange(0, [_entityLightQueryBuffer length])]; + + id commandBuffer = [_commandQueue commandBuffer]; + + __block dispatch_semaphore_t semaphore = _semaphore; + [commandBuffer addCompletedHandler:^(id buffer) { + dispatch_semaphore_signal(semaphore); + }]; + + MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; + passDescriptor.colorAttachments[0].texture = _renderTexture; + passDescriptor.colorAttachments[0].storeAction = MTLStoreActionDontCare; // no longer care about the render texture + passDescriptor.depthAttachment.texture = _depthTexture; + passDescriptor.depthAttachment.loadAction = MTLLoadActionLoad; + passDescriptor.visibilityResultBuffer = _entityLightQueryBuffer; + id encoder = [commandBuffer renderCommandEncoderWithDescriptor:passDescriptor]; + + [encoder setDepthStencilState:_depthStateNoWrite]; + [encoder setRenderPipelineState:_pipelineState]; + [encoder setCullMode:MTLCullModeBack]; + [encoder setVertexBuffer:_vertexBuffer offset:0 atIndex:BufferIndexVertex]; + [encoder setVertexBuffer:_uniformBuffer offset:0 atIndex:BufferIndexUniforms]; + + NSUInteger queryCount = 0; + for (int j = 0; j < 6; ++j) { + GLint x = (j % 3) * 256; + GLint y = (j / 3) * 256; + [encoder setViewport:{x, y, 256, 256, 0, 1}]; + if (j > 0) { + [encoder setVertexBufferOffset:j * sizeof(Uniforms) atIndex:BufferIndexUniforms]; + } + + NSUInteger vertexBufferOffset = _entityVertStart * sizeof(Vertex); + + for (const auto &ent : entities) { + if (_frustums[j].aabbFrustumTest(ent.aabb)) { + [encoder setVertexBufferOffset:vertexBufferOffset atIndex:BufferIndexVertex]; + [encoder setVisibilityResultMode:MTLVisibilityResultModeBoolean offset:queryCount * sizeof(uint64_t)]; + [encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangleStrip + indexCount:20 + indexType:MTLIndexTypeUInt32 + indexBuffer:_aabbIndexBuffer + indexBufferOffset:0]; + } + vertexBufferOffset += 20 * sizeof(Vertex); + ++queryCount; + } + + for (const auto &light : lights) { + if (_frustums[j].pointFrustumTest(light.point)) { + [encoder setVertexBufferOffset:vertexBufferOffset atIndex:BufferIndexVertex]; + [encoder setVisibilityResultMode:MTLVisibilityResultModeBoolean offset:queryCount * sizeof(uint64_t)]; + [encoder drawPrimitives:MTLPrimitiveTypePoint vertexStart:0 vertexCount:1]; + } + vertexBufferOffset += sizeof(Vertex); + ++queryCount; + } + } + + [encoder endEncoding]; + [commandBuffer commit]; + + dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER); + + NSUInteger total = entities.size() + lights.size(); + auto *queries = static_cast([_entityLightQueryBuffer contents]); + for (int i = 0; i < entities.size(); ++i) { + for (int j = 0; j < 6; ++j) { + if (queries[i + j * total] != 0u) { + entityPassFunc(i); + break; + } + } + } + for (int i = 0; i < lights.size(); ++i) { + bool pointInside = totalAABB.pointInside(lights[i].point); + EPVSVisSetState state = pointInside ? EPVSVisSetState::EndOfTree : EPVSVisSetState::OutOfBounds; + if (pointInside) { + for (int j = 0; j < 6; ++j) { + if (queries[entities.size() + i + j * total] != 0u) { + state = EPVSVisSetState::NodeFound; + break; + } + } + } + lightPassFunc(i, state); + } +} +@end + +bool VISIRendererMetal::SetupShaders() { return [view setup]; } + +bool VISIRendererMetal::SetupVertexBuffersAndFormats() { + return [view setupModels:m_models entities:m_entities lights:m_lights]; +} + +void VISIRendererMetal::SetupRenderPass(const zeus::CVector3f &pos) { [view setupRenderPass:pos]; } + +void VISIRendererMetal::RenderPVSOpaque(RGBA8 *bufOut, bool &needTransparent) { + [view renderPVSOpaque:m_models out:bufOut needTransparent:needTransparent]; +} + +void VISIRendererMetal::RenderPVSTransparent(const std::function &passFunc) { + [view renderPVSTransparent:m_models passFunc:passFunc]; +} + +void VISIRendererMetal::RenderPVSEntitiesAndLights(const std::function &passFunc, + const std::function &lightPassFunc) { + [view renderPVSEntities:m_entities + entityPassFunc:passFunc + lights:m_lights + lightPassFunc:lightPassFunc + totalAABB:m_totalAABB]; +} \ No newline at end of file diff --git a/visigen/VISIRendererOpenGL.cpp b/visigen/VISIRendererOpenGL.cpp new file mode 100644 index 000000000..0dbed75a6 --- /dev/null +++ b/visigen/VISIRendererOpenGL.cpp @@ -0,0 +1,426 @@ +#include "VISIRendererOpenGL.hpp" +#include "logvisor/logvisor.hpp" +#include "zeus/CFrustum.hpp" + +static logvisor::Module Log("visigen"); + +static const char* VS = + "#version 330\n" + "layout(location=0) in vec4 posIn;\n" + "layout(location=1) in vec4 colorIn;\n" + "\n" + "uniform UniformBlock\n" + "{\n" + " mat4 xf;\n" + "};\n" + "\n" + "struct VertToFrag\n" + "{\n" + " vec4 color;\n" + "};\n" + "\n" + "out VertToFrag vtf;\n" + "void main()\n" + "{\n" + " vtf.color = colorIn;\n" + " gl_Position = xf * vec4(posIn.xyz, 1.0);\n" + "}\n"; + +static const char* FS = + "#version 330\n" + "struct VertToFrag\n" + "{\n" + " vec4 color;\n" + "};\n" + "\n" + "in VertToFrag vtf;\n" + "layout(location=0) out vec4 colorOut;\n" + "void main()\n" + "{\n" + " colorOut = vtf.color;\n" + "}\n"; + +static const uint32_t AABBIdxs[20] = {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 1, 7, 3, 5, 5, 0, 0, 2, 6, 4}; + +static zeus::CMatrix4f g_Proj; + +static void CalculateProjMatrix() { + float znear = 0.2f; + float zfar = 1000.f; + float tfov = std::tan(zeus::degToRad(90.f * 0.5f)); + float top = znear * tfov; + float bottom = -top; + float right = znear * tfov; + float left = -right; + + float rml = right - left; + float rpl = right + left; + float tmb = top - bottom; + float tpb = top + bottom; + float fpn = zfar + znear; + float fmn = zfar - znear; + + g_Proj = zeus::CMatrix4f(2.f * znear / rml, 0.f, rpl / rml, 0.f, 0.f, 2.f * znear / tmb, tpb / tmb, 0.f, 0.f, 0.f, + -fpn / fmn, -2.f * zfar * znear / fmn, 0.f, 0.f, -1.f, 0.f); +} + +static const zeus::CMatrix4f LookMATs[] = { + {// Forward + 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Backward + -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Up + 1.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Down + 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Left + 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, + {// Right + 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f}, +}; + +bool VISIRendererOpenGL::SetupShaders() { + CalculateProjMatrix(); + + if (glewInit() != GLEW_OK) { + Log.report(logvisor::Error, FMT_STRING("unable to init glew")); + return false; + } + + if (!GLEW_ARB_occlusion_query2) { + Log.report(logvisor::Error, FMT_STRING("GL_ARB_occlusion_query2 extension not present")); + return false; + } + + m_vtxShader = glCreateShader(GL_VERTEX_SHADER); + m_fragShader = glCreateShader(GL_FRAGMENT_SHADER); + m_program = glCreateProgram(); + + glShaderSource(m_vtxShader, 1, &VS, nullptr); + glCompileShader(m_vtxShader); + GLint status; + glGetShaderiv(m_vtxShader, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) { + GLint logLen; + glGetShaderiv(m_vtxShader, GL_INFO_LOG_LENGTH, &logLen); + char* log = (char*)malloc(logLen); + glGetShaderInfoLog(m_vtxShader, logLen, nullptr, log); + Log.report(logvisor::Error, FMT_STRING("unable to compile vert source\n{}\n{}\n"), log, VS); + free(log); + return false; + } + + glShaderSource(m_fragShader, 1, &FS, nullptr); + glCompileShader(m_fragShader); + glGetShaderiv(m_fragShader, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) { + GLint logLen; + glGetShaderiv(m_fragShader, GL_INFO_LOG_LENGTH, &logLen); + char* log = (char*)malloc(logLen); + glGetShaderInfoLog(m_fragShader, logLen, nullptr, log); + Log.report(logvisor::Error, FMT_STRING("unable to compile frag source\n{}\n{}\n"), log, FS); + free(log); + return false; + } + + glAttachShader(m_program, m_vtxShader); + glAttachShader(m_program, m_fragShader); + + glLinkProgram(m_program); + glGetProgramiv(m_program, GL_LINK_STATUS, &status); + if (status != GL_TRUE) { + GLint logLen; + glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &logLen); + char* log = (char*)malloc(logLen); + glGetProgramInfoLog(m_program, logLen, nullptr, log); + Log.report(logvisor::Error, FMT_STRING("unable to link shader program\n{}\n"), log); + free(log); + return false; + } + + glUseProgram(m_program); + m_uniLoc = glGetUniformBlockIndex(m_program, "UniformBlock"); + + glGenBuffers(1, &m_uniformBufferGL); + glBindBuffer(GL_UNIFORM_BUFFER, m_uniformBufferGL); + glBufferData(GL_UNIFORM_BUFFER, sizeof(zeus::CMatrix4f) * 6, nullptr, GL_DYNAMIC_DRAW); + + glGenBuffers(1, &m_aabbIBO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_aabbIBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, 20 * 4, AABBIdxs, GL_STATIC_DRAW); + + glEnable(GL_PRIMITIVE_RESTART); + glPrimitiveRestartIndex(0xffffffff); + + return true; +} + +bool VISIRendererOpenGL::SetupVertexBuffersAndFormats() { + m_modelBindings.resize(m_models.size()); + m_entityBindings.resize(m_entities.size()); + m_lightBindings.resize(m_lights.size()); + + { + auto model = m_models.begin(); + auto modelBinding = m_modelBindings.begin(); + while (model != m_models.end()) { + glGenVertexArrays(1, &modelBinding->vao); + glGenBuffers(1, &modelBinding->vbo); + glGenBuffers(1, &modelBinding->ibo); + + glBindVertexArray(modelBinding->vao); + + glBindBuffer(GL_ARRAY_BUFFER, modelBinding->vbo); + glBufferData(GL_ARRAY_BUFFER, model->verts.size() * sizeof(Model::Vert), model->verts.data(), GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), 0); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), (void*)16); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelBinding->ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, model->idxs.size() * 4, model->idxs.data(), GL_STATIC_DRAW); + + ++model; + ++modelBinding; + } + } + + uint32_t idx = m_models.size(); + { + auto ent = m_entities.begin(); + auto entBinding = m_entityBindings.begin(); + while (ent != m_entities.end()) { + glGenVertexArrays(1, &entBinding->vao); + glGenBuffers(1, &entBinding->vbo); + + glBindVertexArray(entBinding->vao); + + auto verts = AABBToVerts(ent->aabb, ColorForIndex(idx++)); + glBindBuffer(GL_ARRAY_BUFFER, entBinding->vbo); + glBufferData(GL_ARRAY_BUFFER, verts.size() * sizeof(Model::Vert), verts.data(), GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), 0); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), (void*)16); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_aabbIBO); + + ++ent; + ++entBinding; + } + } + + { + auto light = m_lights.begin(); + auto lightBinding = m_lightBindings.begin(); + while (light != m_lights.end()) { + glGenVertexArrays(1, &lightBinding->vao); + glGenBuffers(1, &lightBinding->vbo); + + glBindVertexArray(lightBinding->vao); + + Model::Vert vert; + vert.pos = light->point; + vert.color = ColorForIndex(idx++); + glBindBuffer(GL_ARRAY_BUFFER, lightBinding->vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(Model::Vert), &vert, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), 0); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Model::Vert), (void*)16); + + ++light; + ++lightBinding; + } + } + + m_queryCount = m_models.size() + m_entities.size() + m_lights.size(); + m_queries.reset(new GLuint[m_queryCount]); + m_queryBools.reset(new bool[m_queryCount]); + glGenQueries(GLsizei(m_queryCount), m_queries.get()); + + return true; +} + +void VISIRendererOpenGL::SetupRenderPass(const zeus::CVector3f& pos) { + glViewport(0, 0, 768, 512); + glEnable(GL_CULL_FACE); + glDepthMask(GL_TRUE); + glDepthFunc(GL_LEQUAL); + glEnable(GL_DEPTH_TEST); + glClearColor(0.f, 0.f, 0.f, 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + std::array, 6> m_uniformBuffers{}; + static_assert(sizeof(m_uniformBuffers) == 256 * 6); + glBindBuffer(GL_UNIFORM_BUFFER, m_uniformBufferGL); + for (int j = 0; j < 6; ++j) { + zeus::CMatrix4f mv = LookMATs[j] * zeus::CTransform::Translate(-pos).toMatrix4f(); + *static_cast(static_cast(&m_uniformBuffers[j])) = g_Proj * mv; + m_frustums[j].updatePlanes(mv, g_Proj); + } + glBufferData(GL_UNIFORM_BUFFER, sizeof(m_uniformBuffers), m_uniformBuffers.data(), GL_DYNAMIC_DRAW); + glUniformBlockBinding(m_program, m_uniLoc, 0); +} + +void VISIRendererOpenGL::RenderPVSOpaque(RGBA8* bufOut, bool& needTransparent) { + for (int j = 0; j < 6; ++j) { + GLint x = (j % 3) * 256; + GLint y = (j / 3) * 256; + glViewport(x, y, 256, 256); + + glBindBufferRange(GL_UNIFORM_BUFFER, 0, m_uniformBufferGL, static_cast(256 * j), + sizeof(zeus::CMatrix4f)); + + // Draw frontfaces + glCullFace(GL_BACK); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + auto model = m_models.begin(); + auto modelBinding = m_modelBindings.begin(); + while (model != m_models.end()) { + if (m_frustums[j].aabbFrustumTest(model->aabb)) { + glBindVertexArray(modelBinding->vao); + for (const Model::Surface& surf : model->surfaces) { + // Non-transparents first + if (surf.transparent) { + needTransparent = true; + } else { + GLenum topology = model->topology == hecl::HMDLTopology::TriStrips ? GL_TRIANGLE_STRIP : GL_TRIANGLES; + glDrawElements(topology, surf.count, GL_UNSIGNED_INT, reinterpret_cast(uintptr_t(surf.first * 4))); + } + } + } + ++model; + ++modelBinding; + } + } + + // m_swapFunc(); + glFinish(); + glReadPixels(0, 0, 768, 512, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)bufOut); +} + +void VISIRendererOpenGL::RenderPVSTransparent(const std::function& passFunc) { + glDepthMask(GL_FALSE); + + for (int j = 0; j < 6; ++j) { + GLint x = (j % 3) * 256; + GLint y = (j / 3) * 256; + glViewport(x, y, 256, 256); + + glBindBufferRange(GL_UNIFORM_BUFFER, 0, m_uniformBufferGL, static_cast(256 * j), + sizeof(zeus::CMatrix4f)); + + memset(m_queryBools.get(), 0, m_queryCount); + + int idx = 0; + auto model = m_models.begin(); + auto modelBinding = m_modelBindings.begin(); + while (model != m_models.end()) { + if (m_frustums[j].aabbFrustumTest(model->aabb)) { + glBindVertexArray(modelBinding->vao); + glBeginQuery(GL_ANY_SAMPLES_PASSED_CONSERVATIVE, m_queries[idx]); + m_queryBools[idx] = true; + for (const Model::Surface& surf : model->surfaces) { + // transparents + if (surf.transparent) { + GLenum topology = model->topology == hecl::HMDLTopology::TriStrips ? GL_TRIANGLE_STRIP : GL_TRIANGLES; + glDrawElements(topology, surf.count, GL_UNSIGNED_INT, reinterpret_cast(uintptr_t(surf.first * 4))); + } + } + glEndQuery(GL_ANY_SAMPLES_PASSED_CONSERVATIVE); + } + ++idx; + ++model; + ++modelBinding; + } + + for (int i = 0; i < idx; ++i) { + if (m_queryBools[i]) { + GLint res; + glGetQueryObjectiv(m_queries[i], GL_QUERY_RESULT, &res); + if (res) + passFunc(i); + } + } + } +} + +void VISIRendererOpenGL::RenderPVSEntitiesAndLights(const std::function& passFunc, + const std::function& lightPassFunc) { + glDepthMask(GL_FALSE); + + for (int j = 0; j < 6; ++j) { + GLint x = (j % 3) * 256; + GLint y = (j / 3) * 256; + glViewport(x, y, 256, 256); + + glBindBufferRange(GL_UNIFORM_BUFFER, 0, m_uniformBufferGL, static_cast(256 * j), + sizeof(zeus::CMatrix4f)); + + memset(m_queryBools.get(), 0, m_queryCount); + + uint32_t idx = m_models.size(); + { + auto ent = m_entities.begin(); + auto entBinding = m_entityBindings.begin(); + while (ent != m_entities.end()) { + if (m_frustums[j].aabbFrustumTest(ent->aabb)) { + glBindVertexArray(entBinding->vao); + m_queryBools[idx] = true; + glBeginQuery(GL_ANY_SAMPLES_PASSED_CONSERVATIVE, m_queries[idx]); + glDrawElements(GL_TRIANGLE_STRIP, 20, GL_UNSIGNED_INT, 0); + glEndQuery(GL_ANY_SAMPLES_PASSED_CONSERVATIVE); + } + ++idx; + ++ent; + ++entBinding; + } + } + + { + auto light = m_lights.begin(); + auto lightBinding = m_lightBindings.begin(); + while (light != m_lights.end()) { + if (m_frustums[j].pointFrustumTest(light->point)) { + glBindVertexArray(lightBinding->vao); + m_queryBools[idx] = true; + glBeginQuery(GL_ANY_SAMPLES_PASSED_CONSERVATIVE, m_queries[idx]); + glDrawArrays(GL_POINTS, 0, 1); + glEndQuery(GL_ANY_SAMPLES_PASSED_CONSERVATIVE); + } + ++idx; + ++light; + ++lightBinding; + } + } + + idx = m_models.size(); + for (const Entity& ent : m_entities) { + (void)ent; + if (m_queryBools[idx]) { + GLint res; + glGetQueryObjectiv(m_queries[idx], GL_QUERY_RESULT, &res); + if (res) + passFunc(idx); + } + ++idx; + } + + int lightIdx = 0; + for (const Light& light : m_lights) { + if (m_queryBools[idx]) { + GLint res; + glGetQueryObjectiv(m_queries[idx], GL_QUERY_RESULT, &res); + EPVSVisSetState state = + m_totalAABB.pointInside(light.point) ? EPVSVisSetState::EndOfTree : EPVSVisSetState::OutOfBounds; + if (res && state == EPVSVisSetState::EndOfTree) + state = EPVSVisSetState::NodeFound; + lightPassFunc(lightIdx, state); + } + ++lightIdx; + ++idx; + } + } +} diff --git a/visigen/VISIRendererOpenGL.hpp b/visigen/VISIRendererOpenGL.hpp new file mode 100644 index 000000000..f90accecb --- /dev/null +++ b/visigen/VISIRendererOpenGL.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "VISIRenderer.hpp" +#include "boo/graphicsdev/glew.h" + +#include + +class VISIRendererOpenGL : public VISIRenderer { + GLuint m_vtxShader, m_fragShader, m_program, m_uniLoc; + GLuint m_uniformBufferGL; + GLuint m_aabbIBO; + + std::array m_frustums; + + struct ModelBinding { + GLuint vbo, ibo, vao; + }; + struct EntityBinding { + GLuint vbo, vao; + }; + struct LightBinding { + GLuint vbo, vao; + }; + std::vector m_modelBindings; + std::vector m_entityBindings; + std::vector m_lightBindings; + + size_t m_queryCount; + std::unique_ptr m_queries; + std::unique_ptr m_queryBools; + + bool SetupShaders() override; + bool SetupVertexBuffersAndFormats() override; + void SetupRenderPass(const zeus::CVector3f& pos) override; + +public: + VISIRendererOpenGL(int argc, const hecl::SystemChar** argv) : VISIRenderer(argc, argv) {} + void RenderPVSOpaque(RGBA8* bufOut, bool& needTransparent) override; + void RenderPVSTransparent(const std::function& passFunc) override; + void RenderPVSEntitiesAndLights(const std::function& passFunc, + const std::function& lightPassFunc) override; +}; \ No newline at end of file