diff --git a/app/src/main/KDE.xcodeproj/project.pbxproj b/app/src/main/KDE.xcodeproj/project.pbxproj index 3ec4e07..3764ea9 100644 --- a/app/src/main/KDE.xcodeproj/project.pbxproj +++ b/app/src/main/KDE.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + FB239F6B2A51983C0084874D /* IOSAssetManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = FB239F6A2A51983C0084874D /* IOSAssetManager.mm */; }; + FB239F6F2A519BE00084874D /* assets in Resources */ = {isa = PBXBuildFile; fileRef = FB239F6E2A519BE00084874D /* assets */; }; FBA9A7502A50790A00F960DA /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = FBA9A74F2A50790A00F960DA /* AppDelegate.m */; }; FBA9A7532A50790A00F960DA /* MetalViewDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = FBA9A7522A50790A00F960DA /* MetalViewDelegate.mm */; }; FBA9A7562A50790A00F960DA /* GameViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = FBA9A7552A50790A00F960DA /* GameViewController.mm */; }; @@ -27,6 +29,9 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + FB239F692A5197830084874D /* IOSAssetManager.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; path = IOSAssetManager.h; sourceTree = ""; }; + FB239F6A2A51983C0084874D /* IOSAssetManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = IOSAssetManager.mm; sourceTree = ""; }; + FB239F6E2A519BE00084874D /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = assets; sourceTree = ""; }; FBA9A7342A5078B800F960DA /* AssetManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AssetManager.h; path = cpp/AssetManager.h; sourceTree = ""; }; FBA9A7352A5078B800F960DA /* Font.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Font.cpp; path = cpp/Font.cpp; sourceTree = ""; }; FBA9A7372A5078B800F960DA /* Hash.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Hash.h; path = cpp/Hash.h; sourceTree = ""; }; @@ -127,6 +132,8 @@ FBA9A7622A50790A00F960DA /* main.m */, FBA9A76C2A5096C500F960DA /* MetalGfxInterface.h */, FBA9A76F2A5098E800F960DA /* MetalGfxInterface.mm */, + FB239F692A5197830084874D /* IOSAssetManager.h */, + FB239F6A2A51983C0084874D /* IOSAssetManager.mm */, ); path = m; sourceTree = ""; @@ -152,6 +159,7 @@ FBC6DF7A2A50782C00CA10E7 = { isa = PBXGroup; children = ( + FB239F6E2A519BE00084874D /* assets */, FBA9A7462A5078DC00F960DA /* m */, FBA9A7332A50789900F960DA /* cpp */, FBA9A74C2A50790A00F960DA /* Products */, @@ -217,6 +225,7 @@ buildActionMask = 2147483647; files = ( FBA9A7612A50790A00F960DA /* LaunchScreen.storyboard in Resources */, + FB239F6F2A519BE00084874D /* assets in Resources */, FBA9A75E2A50790A00F960DA /* Assets.xcassets in Resources */, FBA9A75C2A50790A00F960DA /* Main.storyboard in Resources */, ); @@ -235,6 +244,7 @@ FBA9A76B2A5079BC00F960DA /* Texture.cpp in Sources */, FBA9A7762A50A58000F960DA /* Renderer.cpp in Sources */, FBA9A7682A50795000F960DA /* StringRepository.cpp in Sources */, + FB239F6B2A51983C0084874D /* IOSAssetManager.mm in Sources */, FBA9A7702A5098E800F960DA /* MetalGfxInterface.mm in Sources */, FBA9A7532A50790A00F960DA /* MetalViewDelegate.mm in Sources */, FBA9A76A2A50795700F960DA /* Hash.cpp in Sources */, diff --git a/app/src/main/KDE.xcodeproj/project.xcworkspace/xcuserdata/ronjaenseleit.xcuserdatad/UserInterfaceState.xcuserstate b/app/src/main/KDE.xcodeproj/project.xcworkspace/xcuserdata/ronjaenseleit.xcuserdatad/UserInterfaceState.xcuserstate index 20b0ba4..75faa96 100644 Binary files a/app/src/main/KDE.xcodeproj/project.xcworkspace/xcuserdata/ronjaenseleit.xcuserdatad/UserInterfaceState.xcuserstate and b/app/src/main/KDE.xcodeproj/project.xcworkspace/xcuserdata/ronjaenseleit.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/app/src/main/KDE.xcodeproj/xcuserdata/ronjaenseleit.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/app/src/main/KDE.xcodeproj/xcuserdata/ronjaenseleit.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 4c1bb89..e264d4e 100644 --- a/app/src/main/KDE.xcodeproj/xcuserdata/ronjaenseleit.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/app/src/main/KDE.xcodeproj/xcuserdata/ronjaenseleit.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -20,5 +20,116 @@ landmarkType = "0"> + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/cpp/Renderer.cpp b/app/src/main/cpp/Renderer.cpp index d1bc815..77ed149 100644 --- a/app/src/main/cpp/Renderer.cpp +++ b/app/src/main/cpp/Renderer.cpp @@ -171,6 +171,7 @@ void Renderer::addFontRect(float x, void Renderer::renderFrame(float width, float height) { + // This is done automatically under iOS and does not require a call. #if defined(_WIN32) || defined(__ANDROID__) openGLGfxRenderFrame((OpenGLGfx*)m_gfx, width, height); #endif @@ -179,7 +180,7 @@ void Renderer::renderFrame(float width, float height) void Renderer::getTextureSize(KDEStringHandle texture, unsigned int* w, unsigned int* h) { #ifdef __APPLE__ - + // TODO #else openGLGfxGetTextureSize((OpenGLGfx*)m_gfx, texture, w, h); #endif diff --git a/app/src/main/cpp/Renderer.h b/app/src/main/cpp/Renderer.h index a45ef14..23bc9f1 100644 --- a/app/src/main/cpp/Renderer.h +++ b/app/src/main/cpp/Renderer.h @@ -175,6 +175,8 @@ public: KDEStringHandle texture, unsigned int* w, unsigned int* h); + + void* getGfx() { return m_gfx; } private: Renderer(void* gfx); diff --git a/app/src/main/cpp/Texture.cpp b/app/src/main/cpp/Texture.cpp index 970e5c1..0db40a6 100644 --- a/app/src/main/cpp/Texture.cpp +++ b/app/src/main/cpp/Texture.cpp @@ -1,5 +1,10 @@ #include "Texture.h" #include "Profiling.h" +#include "Renderer.h" + +#ifdef __APPLE__ +#include "../m/MetalGfxInterface.h" +#endif #if defined(__ANDROID__) || defined(_WIN32) Texture::Texture() : m_texture(0) {} @@ -69,6 +74,35 @@ void Texture::bind() const glBindTexture(GL_TEXTURE_2D, m_texture); } +#else + +Texture::Texture() : m_texture(nullptr) {} + +Texture::Texture(unsigned int width, unsigned int height, const void* data) + : width_px(width), height_px(height) +{ + m_texture = metalGfxCreateTexture(Renderer::ptr->getGfx(), data, width, height, MetalGfxTextureFormat_RGBA); +} + +Texture::Texture(unsigned int width, unsigned int height, const void* data, int dummy) + : width_px(width), height_px(height) +{ + m_texture = metalGfxCreateTexture(Renderer::ptr->getGfx(), data, width, height, MetalGfxTextureFormat_Red); +} + +void Texture::destroy() +{ + metalGfxDestroyTexture(m_texture); + m_texture = nullptr; +} + +void Texture::bind() const +{ + /* This is a no-op under metal? */ +} + +#endif + void Texture::getTextureSize(unsigned int* w, unsigned int* h) { ZoneScoped; @@ -79,29 +113,3 @@ void Texture::getTextureSize(unsigned int* w, unsigned int* h) *h = height_px; } } - -#else - -Texture::Texture() {} - -Texture::Texture(unsigned int width, unsigned int height, const void* data) -{ -} - -Texture::Texture(unsigned int width, unsigned int height, const void* data, int dummy) -{ -} - -void Texture::destroy() -{ -} - -void Texture::bind() const -{ -} - -void Texture::getTextureSize(unsigned int* w, unsigned int* h) -{ -} - -#endif diff --git a/app/src/main/cpp/Texture.h b/app/src/main/cpp/Texture.h index 5b74cf8..7ed370f 100644 --- a/app/src/main/cpp/Texture.h +++ b/app/src/main/cpp/Texture.h @@ -37,9 +37,15 @@ public: unsigned int* w, unsigned int* h); +#ifdef __APPLE__ + void* getObj() { return m_texture; } +#endif + private: #if defined(_WIN32) || defined(__ANDROID__) GLuint m_texture; +#elif defined(__APPLE__) + void* m_texture; #endif int width_px; diff --git a/app/src/main/m/GameViewController.mm b/app/src/main/m/GameViewController.mm index 9656c42..d8469d9 100644 --- a/app/src/main/m/GameViewController.mm +++ b/app/src/main/m/GameViewController.mm @@ -9,6 +9,7 @@ #import "MetalViewDelegate.h" #include "../cpp/Renderer.h" +#include "IOSAssetManager.h" @implementation GameViewController { @@ -33,10 +34,9 @@ return; } + IOSAssetManager::create(); + _renderer = [[MetalViewDelegate alloc] initWithMetalKitView:_view]; - - [_renderer mtkView:_view drawableSizeWillChange:_view.bounds.size]; - _view.delegate = _renderer; Renderer::create((__bridge void*)_renderer); diff --git a/app/src/main/m/IOSAssetManager.h b/app/src/main/m/IOSAssetManager.h new file mode 100644 index 0000000..7d88fd3 --- /dev/null +++ b/app/src/main/m/IOSAssetManager.h @@ -0,0 +1,17 @@ +#ifndef KRIMI_DINNER_ENGINE_IOSAssetManager_h +#define KRIMI_DINNER_ENGINE_IOSAssetManager_h + +#include "../cpp/AssetManager.h" + +class IOSAssetManager : public AssetManager +{ +public: + static void create(); + + bool loadFile(const char* path, FileBuffer* p_file_buffer) override; + + void releaseFileBuffer(FileBuffer& fb) override; + +}; + +#endif diff --git a/app/src/main/m/IOSAssetManager.mm b/app/src/main/m/IOSAssetManager.mm new file mode 100644 index 0000000..22c22ed --- /dev/null +++ b/app/src/main/m/IOSAssetManager.mm @@ -0,0 +1,35 @@ +#include "IOSAssetManager.h" + +#import +#include +#include + +void IOSAssetManager::create() +{ + ptr = new IOSAssetManager; +} + +bool IOSAssetManager::loadFile(const char* path, FileBuffer* p_file_buf) +{ + NSString* search_path = [NSString stringWithUTF8String:path]; + NSString* full_path = [[NSBundle mainBundle] pathForResource:search_path ofType:@"" inDirectory:@"assets"]; + NSData* content = [[NSFileManager defaultManager] contentsAtPath: full_path]; + + NSUInteger length = [content length]; + + void *buf = malloc((size_t)length); + if (!buf) + return false; + [content getBytes: buf length: length]; + p_file_buf->data = buf; + p_file_buf->size = (size_t)length; + p_file_buf->internal = NULL; + + return true; +} + +void IOSAssetManager::releaseFileBuffer(FileBuffer& fb) +{ + free((void*)fb.data); + memset(&fb, 0, sizeof(fb)); +} diff --git a/app/src/main/m/MetalGfxInterface.h b/app/src/main/m/MetalGfxInterface.h index 1a1b4d1..c331431 100644 --- a/app/src/main/m/MetalGfxInterface.h +++ b/app/src/main/m/MetalGfxInterface.h @@ -8,4 +8,14 @@ void metalGfxPushRect(void* metalGfxObj, KDERect rect, KDEStringHandle texture); void metalGfxPushFontRect(void* metalGfxObj, KDERect rect, float char_height, KDEStringHandle font, KDEStringHandle key); +typedef enum MetalGfxTextureFormat +{ + MetalGfxTextureFormat_RGBA, + MetalGfxTextureFormat_Red, +} MetalGfxTextureFormat; + +void* metalGfxCreateTexture(void* metalGfxObj, const void* pixels, unsigned int width, unsigned int height, MetalGfxTextureFormat format); + +void metalGfxDestroyTexture(void* textureObj); + #endif diff --git a/app/src/main/m/MetalGfxInterface.mm b/app/src/main/m/MetalGfxInterface.mm index e5a220a..81c0cac 100644 --- a/app/src/main/m/MetalGfxInterface.mm +++ b/app/src/main/m/MetalGfxInterface.mm @@ -1,5 +1,8 @@ #import #import "MetalViewDelegate.h" +#import + +#import "MetalGfxInterface.h" void metalGfxPushRect(void* metalGfxObj, KDERect rect, KDEStringHandle texture) { @@ -10,3 +13,17 @@ void metalGfxPushFontRect(void* metalGfxObj, KDERect rect, float char_height, KD { [(__bridge id)metalGfxObj pushRect:rect withFont:font withCharHeight:char_height withKey:key]; } + +void* metalGfxCreateTexture(void* metalGfxObj, const void* pixels, unsigned int width, unsigned int height, MetalGfxTextureFormat format) +{ + MTLPixelFormat pxFormat = (format == MetalGfxTextureFormat_RGBA) + ? MTLPixelFormatRGBA8Uint + : MTLPixelFormatR8Uint; + return (__bridge_retained void*)[(__bridge id)metalGfxObj createTexture:pixels width:width height:height format:pxFormat]; +} + +void metalGfxDestroyTexture(void* textureObj) +{ + id texture = (__bridge_transfer id)textureObj; + // Texture should be dropped here +} diff --git a/app/src/main/m/MetalViewDelegate.h b/app/src/main/m/MetalViewDelegate.h index 7b5fc96..2883cfb 100644 --- a/app/src/main/m/MetalViewDelegate.h +++ b/app/src/main/m/MetalViewDelegate.h @@ -20,5 +20,7 @@ -(void)pushRect:(KDERect)rect withFont:(KDEStringHandle)font withCharHeight:(float)char_height withKey:(KDEStringHandle)key; +-(nonnull id)createTexture:(nonnull const void*)data width:(unsigned int)width height:(unsigned int)height format:(MTLPixelFormat)format; + @end diff --git a/app/src/main/m/MetalViewDelegate.mm b/app/src/main/m/MetalViewDelegate.mm index 886a894..54eb417 100644 --- a/app/src/main/m/MetalViewDelegate.mm +++ b/app/src/main/m/MetalViewDelegate.mm @@ -1,12 +1,7 @@ -// -// Renderer.m -// KDE -// -// Created by Ronja Enseleit on 01.07.23. -// - #import #import +#include +#include #import "MetalViewDelegate.h" @@ -14,6 +9,7 @@ #import "ShaderTypes.h" #include "../cpp/Renderer.h" +#include "IOSAssetManager.h" static const NSUInteger MaxBuffersInFlight = 3; @@ -22,20 +18,25 @@ static const NSUInteger MaxBuffersInFlight = 3; dispatch_semaphore_t _inFlightSemaphore; id _device; id _commandQueue; - + id _dynamicUniformBuffer[MaxBuffersInFlight]; id _pipelineState; id _depthState; - id _colorMap; + id _dummyTexture; MTLVertexDescriptor *_mtlVertexDescriptor; - - uint8_t _uniformBufferIndex; - - matrix_float4x4 _projectionMatrix; - - float _rotation; - - MTKMesh *_mesh; + + vector_float2 _resolution; + + uint8_t _bufferIndex; + + id _vertexBuffers[MaxBuffersInFlight]; + // In number of rects + size_t _vertexBufferCapacities[MaxBuffersInFlight]; + + std::vector _drawRects; + std::vector> _drawTextures; + + std::map _textures; } -(nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)view; @@ -46,7 +47,6 @@ static const NSUInteger MaxBuffersInFlight = 3; _device = view.device; _inFlightSemaphore = dispatch_semaphore_create(MaxBuffersInFlight); [self _loadMetalWithView:view]; - [self _loadAssets]; } return self; @@ -55,35 +55,47 @@ static const NSUInteger MaxBuffersInFlight = 3; - (void)_loadMetalWithView:(nonnull MTKView *)view; { /// Load Metal state objects and initialize renderer dependent view properties - + view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; view.colorPixelFormat = MTLPixelFormatBGRA8Unorm_sRGB; view.sampleCount = 1; - + + _resolution = vector2((float)[view drawableSize].width, (float)[view drawableSize].height); + _mtlVertexDescriptor = [[MTLVertexDescriptor alloc] init]; - - _mtlVertexDescriptor.attributes[VertexAttributePosition].format = MTLVertexFormatFloat3; - _mtlVertexDescriptor.attributes[VertexAttributePosition].offset = 0; - _mtlVertexDescriptor.attributes[VertexAttributePosition].bufferIndex = BufferIndexMeshPositions; - - _mtlVertexDescriptor.attributes[VertexAttributeTexcoord].format = MTLVertexFormatFloat2; - _mtlVertexDescriptor.attributes[VertexAttributeTexcoord].offset = 0; - _mtlVertexDescriptor.attributes[VertexAttributeTexcoord].bufferIndex = BufferIndexMeshGenerics; - - _mtlVertexDescriptor.layouts[BufferIndexMeshPositions].stride = 12; - _mtlVertexDescriptor.layouts[BufferIndexMeshPositions].stepRate = 1; - _mtlVertexDescriptor.layouts[BufferIndexMeshPositions].stepFunction = MTLVertexStepFunctionPerVertex; - - _mtlVertexDescriptor.layouts[BufferIndexMeshGenerics].stride = 8; - _mtlVertexDescriptor.layouts[BufferIndexMeshGenerics].stepRate = 1; - _mtlVertexDescriptor.layouts[BufferIndexMeshGenerics].stepFunction = MTLVertexStepFunctionPerVertex; - + + _mtlVertexDescriptor.attributes[VertexAttributeDstP0].format = MTLVertexFormatFloat2; + _mtlVertexDescriptor.attributes[VertexAttributeDstP0].offset = 0; + _mtlVertexDescriptor.attributes[VertexAttributeDstP0].bufferIndex = BufferIndexRects; + + _mtlVertexDescriptor.attributes[VertexAttributeDstP1].format = MTLVertexFormatFloat2; + _mtlVertexDescriptor.attributes[VertexAttributeDstP1].offset = offsetof(KDERect, dst_p1_x); + _mtlVertexDescriptor.attributes[VertexAttributeDstP1].bufferIndex = BufferIndexRects; + + _mtlVertexDescriptor.attributes[VertexAttributeSrcP0].format = MTLVertexFormatFloat2; + _mtlVertexDescriptor.attributes[VertexAttributeSrcP0].offset = offsetof(KDERect, src_p0_x); + _mtlVertexDescriptor.attributes[VertexAttributeSrcP0].bufferIndex = BufferIndexRects; + + _mtlVertexDescriptor.attributes[VertexAttributeSrcP1].format = MTLVertexFormatFloat2; + _mtlVertexDescriptor.attributes[VertexAttributeSrcP1].offset = offsetof(KDERect, src_p1_x); + _mtlVertexDescriptor.attributes[VertexAttributeSrcP1].bufferIndex = BufferIndexRects; + + _mtlVertexDescriptor.attributes[VertexAttributeColor].format = MTLVertexFormatFloat4; + _mtlVertexDescriptor.attributes[VertexAttributeColor].offset = offsetof(KDERect, r); + _mtlVertexDescriptor.attributes[VertexAttributeColor].bufferIndex = BufferIndexRects; + + _mtlVertexDescriptor.attributes[VertexAttributeExpandR].format = MTLVertexFormatInt; + _mtlVertexDescriptor.attributes[VertexAttributeExpandR].offset = offsetof(KDERect, expand_r); + _mtlVertexDescriptor.attributes[VertexAttributeExpandR].bufferIndex = BufferIndexRects; + + _mtlVertexDescriptor.layouts[BufferIndexRects].stride = sizeof(KDERect); + _mtlVertexDescriptor.layouts[BufferIndexRects].stepRate = 1; + _mtlVertexDescriptor.layouts[BufferIndexRects].stepFunction = MTLVertexStepFunctionPerInstance; + id defaultLibrary = [_device newDefaultLibrary]; - id vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"]; - id fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"]; - + MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; pipelineStateDescriptor.label = @"MyPipeline"; pipelineStateDescriptor.rasterSampleCount = view.sampleCount; @@ -93,83 +105,39 @@ static const NSUInteger MaxBuffersInFlight = 3; pipelineStateDescriptor.colorAttachments[0].pixelFormat = view.colorPixelFormat; pipelineStateDescriptor.depthAttachmentPixelFormat = view.depthStencilPixelFormat; pipelineStateDescriptor.stencilAttachmentPixelFormat = view.depthStencilPixelFormat; - + NSError *error = NULL; _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error]; if (!_pipelineState) { NSLog(@"Failed to created pipeline state, error %@", error); } - + MTLDepthStencilDescriptor *depthStateDesc = [[MTLDepthStencilDescriptor alloc] init]; depthStateDesc.depthCompareFunction = MTLCompareFunctionLess; - depthStateDesc.depthWriteEnabled = YES; + depthStateDesc.depthWriteEnabled = NO; _depthState = [_device newDepthStencilStateWithDescriptor:depthStateDesc]; - + for(NSUInteger i = 0; i < MaxBuffersInFlight; i++) { _dynamicUniformBuffer[i] = [_device newBufferWithLength:sizeof(Uniforms) options:MTLResourceStorageModeShared]; - + _dynamicUniformBuffer[i].label = @"UniformBuffer"; + + _vertexBufferCapacities[i] = 256; + _vertexBuffers[i] = [_device newBufferWithLength:sizeof(Rect) * _vertexBufferCapacities[i] + options:MTLResourceStorageModeShared]; } - + _commandQueue = [_device newCommandQueue]; + + // Create the dummy texture (1x1 px white) + uint8_t bytes[4] = { 255, 255, 255, 255 }; + _dummyTexture = [self createTexture:bytes width:1 height:1 format:MTLPixelFormatRGBA8Uint]; } -- (void)_loadAssets -{ - /// Load assets into metal objects - - NSError *error; - - MTKMeshBufferAllocator *metalAllocator = [[MTKMeshBufferAllocator alloc] - initWithDevice: _device]; - - MDLMesh *mdlMesh = [MDLMesh newBoxWithDimensions:(vector_float3){4, 4, 4} - segments:(vector_uint3){2, 2, 2} - geometryType:MDLGeometryTypeTriangles - inwardNormals:NO - allocator:metalAllocator]; - - MDLVertexDescriptor *mdlVertexDescriptor = - MTKModelIOVertexDescriptorFromMetal(_mtlVertexDescriptor); - - mdlVertexDescriptor.attributes[VertexAttributePosition].name = MDLVertexAttributePosition; - mdlVertexDescriptor.attributes[VertexAttributeTexcoord].name = MDLVertexAttributeTextureCoordinate; - - mdlMesh.vertexDescriptor = mdlVertexDescriptor; - - _mesh = [[MTKMesh alloc] initWithMesh:mdlMesh - device:_device - error:&error]; - - if(!_mesh || error) - { - NSLog(@"Error creating MetalKit mesh %@", error.localizedDescription); - } - - MTKTextureLoader* textureLoader = [[MTKTextureLoader alloc] initWithDevice:_device]; - - NSDictionary *textureLoaderOptions = - @{ - MTKTextureLoaderOptionTextureUsage : @(MTLTextureUsageShaderRead), - MTKTextureLoaderOptionTextureStorageMode : @(MTLStorageModePrivate) - }; - - _colorMap = [textureLoader newTextureWithName:@"ColorMap" - scaleFactor:1.0 - bundle:nil - options:textureLoaderOptions - error:&error]; - - if(!_colorMap || error) - { - NSLog(@"Error creating texture %@", error.localizedDescription); - } -} - -- (void)_updateGameState +- (NSUInteger)_updateGameState { // Demo code static float x = 1.f; @@ -188,21 +156,27 @@ static const NSUInteger MaxBuffersInFlight = 3; } #endif - Renderer::ptr->addRect(100, 100, 500, 500, 0.3f, 0.3f, 0.3f, 1.f); + Renderer::ptr->addRect(100, 100, 500, 500, 1.f, 1.f, 1.f, 1.f, StringRepository::global->internString("smiley_PNG42.png")); //Renderer::ptr->addRect(m_smiley_pos.x, m_smiley_pos.y, 500, 500, 0.f, x * x, 1.f - x * x, 1.f, m_smiley); + // Upload rects + if (_drawRects.size() > _vertexBufferCapacities[_bufferIndex]) { + size_t newCap = ((_drawRects.size() + 63) / 64) * 64; + _vertexBuffers[_bufferIndex] = [_device newBufferWithLength:sizeof(KDERect) * newCap + options:MTLResourceStorageModeShared]; + _vertexBufferCapacities[_bufferIndex] = newCap; + } + memcpy(_vertexBuffers[_bufferIndex].contents, + _drawRects.data(), + _drawRects.size() * sizeof(KDERect)); + NSUInteger rectCount = (NSUInteger)_drawRects.size(); + _drawRects.clear(); + /// Update any game state before encoding renderint commands to our drawable - Uniforms * uniforms = (Uniforms*)_dynamicUniformBuffer[_uniformBufferIndex].contents; - - uniforms->projectionMatrix = _projectionMatrix; - - vector_float3 rotationAxis = {1, 1, 0}; - matrix_float4x4 modelMatrix = matrix4x4_rotation(_rotation, rotationAxis); - matrix_float4x4 viewMatrix = matrix4x4_translation(0.0, 0.0, -8.0); - - uniforms->modelViewMatrix = matrix_multiply(viewMatrix, modelMatrix); - - _rotation += .01; + Uniforms * uniforms = (Uniforms*)_dynamicUniformBuffer[_bufferIndex].contents; + uniforms->resolution = _resolution; + + return rectCount; } - (void)drawInMTKView:(nonnull MTKView *)view @@ -211,7 +185,7 @@ static const NSUInteger MaxBuffersInFlight = 3; dispatch_semaphore_wait(_inFlightSemaphore, DISPATCH_TIME_FOREVER); - _uniformBufferIndex = (_uniformBufferIndex + 1) % MaxBuffersInFlight; + _bufferIndex = (_bufferIndex + 1) % MaxBuffersInFlight; id commandBuffer = [_commandQueue commandBuffer]; commandBuffer.label = @"MyCommand"; @@ -222,8 +196,8 @@ static const NSUInteger MaxBuffersInFlight = 3; dispatch_semaphore_signal(block_sema); }]; - [self _updateGameState]; - + NSUInteger rectCount = [self _updateGameState]; + /// Delay getting the currentRenderPassDescriptor until absolutely needed. This avoids /// holding onto the drawable and blocking the display pipeline any longer than necessary MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; @@ -231,55 +205,72 @@ static const NSUInteger MaxBuffersInFlight = 3; if(renderPassDescriptor != nil) { /// Final pass rendering code here - id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; renderEncoder.label = @"MyRenderEncoder"; - [renderEncoder pushDebugGroup:@"DrawBox"]; + [renderEncoder pushDebugGroup:@"DrawRects"]; [renderEncoder setFrontFacingWinding:MTLWindingCounterClockwise]; - [renderEncoder setCullMode:MTLCullModeBack]; + [renderEncoder setCullMode:MTLCullModeNone]; [renderEncoder setRenderPipelineState:_pipelineState]; [renderEncoder setDepthStencilState:_depthState]; - [renderEncoder setVertexBuffer:_dynamicUniformBuffer[_uniformBufferIndex] + [renderEncoder setVertexBuffer:_dynamicUniformBuffer[_bufferIndex] offset:0 atIndex:BufferIndexUniforms]; - [renderEncoder setFragmentBuffer:_dynamicUniformBuffer[_uniformBufferIndex] + [renderEncoder setFragmentBuffer:_dynamicUniformBuffer[_bufferIndex] offset:0 atIndex:BufferIndexUniforms]; - for (NSUInteger bufferIndex = 0; bufferIndex < _mesh.vertexBuffers.count; bufferIndex++) - { - MTKMeshBuffer *vertexBuffer = _mesh.vertexBuffers[bufferIndex]; - if((NSNull*)vertexBuffer != [NSNull null]) - { - [renderEncoder setVertexBuffer:vertexBuffer.buffer - offset:vertexBuffer.offset - atIndex:bufferIndex]; + [renderEncoder setVertexBuffer:_vertexBuffers[_bufferIndex] + offset:0 + atIndex:BufferIndexRects]; + + size_t start = 0; + id currentTexture = _drawTextures[0]; + while (start < rectCount) { + for (size_t i = start; i < rectCount; ++i) { + id texture = _drawTextures[i]; + if (texture != currentTexture) { + size_t count = i - start; + [renderEncoder setFragmentTexture:currentTexture + atIndex:TextureIndexColor]; + + [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle + vertexStart:0 + vertexCount:6 + instanceCount:count + baseInstance:start]; + start = i; + currentTexture = texture; + break; + } + else if (i == rectCount - 1) { + // just draw it + const size_t count = rectCount - start; + [renderEncoder setFragmentTexture:currentTexture + atIndex:TextureIndexColor]; + + [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle + vertexStart:0 + vertexCount:6 + instanceCount:count + baseInstance:start]; + start = rectCount; + break; + } } } - - [renderEncoder setFragmentTexture:_colorMap - atIndex:TextureIndexColor]; - - for(MTKSubmesh *submesh in _mesh.submeshes) - { - [renderEncoder drawIndexedPrimitives:submesh.primitiveType - indexCount:submesh.indexCount - indexType:submesh.indexType - indexBuffer:submesh.indexBuffer.buffer - indexBufferOffset:submesh.indexBuffer.offset]; - } - + [renderEncoder popDebugGroup]; [renderEncoder endEncoding]; [commandBuffer presentDrawable:view.currentDrawable]; } + _drawTextures.clear(); [commandBuffer commit]; } @@ -287,62 +278,66 @@ static const NSUInteger MaxBuffersInFlight = 3; - (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size { /// Respond to drawable size or orientation changes here - - float aspect = size.width / (float)size.height; - _projectionMatrix = matrix_perspective_right_hand(65.0f * (M_PI / 180.0f), aspect, 0.1f, 100.0f); + _resolution.x = (float)size.width; + _resolution.y = (float)size.height; } - (void)pushRect:(KDERect)rect withTexture:(KDEStringHandle)texture { - // TODO + Texture tex; + if (texture != 0) { + if (_textures.find(texture) == _textures.end()) { + if (!AssetManager::ptr->loadTexture(StringRepository::global->getString(texture), &tex)) { + NSLog(@"Failed to load a texture"); + return; + } + _textures.insert(std::make_pair(texture, tex)); + } + else { + tex = _textures[texture]; + } + _drawTextures.push_back((__bridge id)tex.getObj()); + } + else { + _drawTextures.push_back(_dummyTexture); + } + _drawRects.push_back(rect); } - (void)pushRect:(KDERect)rect withFont:(KDEStringHandle)font withCharHeight:(float)char_height withKey:(KDEStringHandle)key { - // TODO + Texture tex; + if (key != 0) { + if (_textures.find(key) == _textures.end()) { + if (!AssetManager::ptr->loadFontBitmap(StringRepository::global->getString(font), char_height, &tex)) { + NSLog(@"Failed to load font"); + return; + } + _textures.insert(std::make_pair(key, tex)); + } + else { + tex = _textures[key]; + } + _drawTextures.push_back((__bridge id)tex.getObj()); + } + else { + _drawTextures.push_back(_dummyTexture); + } + _drawRects.push_back(rect); } -#pragma mark Matrix Math Utilities -matrix_float4x4 matrix4x4_translation(float tx, float ty, float tz) +-(id)createTexture:(nonnull const void*)data width:(unsigned int)width height:(unsigned int)height format:(MTLPixelFormat)format; { - return (matrix_float4x4) {{ - { 1, 0, 0, 0 }, - { 0, 1, 0, 0 }, - { 0, 0, 1, 0 }, - { tx, ty, tz, 1 } - }}; -} - -static matrix_float4x4 matrix4x4_rotation(float radians, vector_float3 axis) -{ - axis = vector_normalize(axis); - float ct = cosf(radians); - float st = sinf(radians); - float ci = 1 - ct; - float x = axis.x, y = axis.y, z = axis.z; - - return (matrix_float4x4) {{ - { ct + x * x * ci, y * x * ci + z * st, z * x * ci - y * st, 0}, - { x * y * ci - z * st, ct + y * y * ci, z * y * ci + x * st, 0}, - { x * z * ci + y * st, y * z * ci - x * st, ct + z * z * ci, 0}, - { 0, 0, 0, 1} - }}; -} - -matrix_float4x4 matrix_perspective_right_hand(float fovyRadians, float aspect, float nearZ, float farZ) -{ - float ys = 1 / tanf(fovyRadians * 0.5); - float xs = ys / aspect; - float zs = farZ / (nearZ - farZ); - - return (matrix_float4x4) {{ - { xs, 0, 0, 0 }, - { 0, ys, 0, 0 }, - { 0, 0, zs, -1 }, - { 0, 0, nearZ * zs, 0 } - }}; + MTLTextureDescriptor* desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format width:(NSUInteger)width height:(NSUInteger)height mipmapped:NO]; + + MTLRegion region = { {0, 0, 0}, {width, height, 1}}; + + id texture = [_device newTextureWithDescriptor: desc]; + NSUInteger bytesPerRow = 4 * width; + [texture replaceRegion:region mipmapLevel:0 withBytes:data bytesPerRow:bytesPerRow]; + return texture; } @end diff --git a/app/src/main/m/ShaderTypes.h b/app/src/main/m/ShaderTypes.h index ddd9121..c60491c 100644 --- a/app/src/main/m/ShaderTypes.h +++ b/app/src/main/m/ShaderTypes.h @@ -23,15 +23,18 @@ typedef NSInteger EnumBackingType; typedef NS_ENUM(EnumBackingType, BufferIndex) { - BufferIndexMeshPositions = 0, - BufferIndexMeshGenerics = 1, - BufferIndexUniforms = 2 + BufferIndexRects = 0, + BufferIndexUniforms = 1 }; typedef NS_ENUM(EnumBackingType, VertexAttribute) { - VertexAttributePosition = 0, - VertexAttributeTexcoord = 1, + VertexAttributeDstP0 = 0, + VertexAttributeDstP1 = 1, + VertexAttributeSrcP0 = 2, + VertexAttributeSrcP1 = 3, + VertexAttributeColor = 4, + VertexAttributeExpandR = 5 }; typedef NS_ENUM(EnumBackingType, TextureIndex) @@ -41,8 +44,7 @@ typedef NS_ENUM(EnumBackingType, TextureIndex) typedef struct { - matrix_float4x4 projectionMatrix; - matrix_float4x4 modelViewMatrix; + vector_float2 resolution; } Uniforms; #endif /* ShaderTypes_h */ diff --git a/app/src/main/m/Shaders.metal b/app/src/main/m/Shaders.metal index 1fb5a73..68ace7f 100644 --- a/app/src/main/m/Shaders.metal +++ b/app/src/main/m/Shaders.metal @@ -17,37 +17,71 @@ using namespace metal; typedef struct { - float3 position [[attribute(VertexAttributePosition)]]; - float2 texCoord [[attribute(VertexAttributeTexcoord)]]; + float2 dstP0 [[attribute(VertexAttributeDstP0)]]; + float2 dstP1 [[attribute(VertexAttributeDstP1)]]; + float2 srcP0 [[attribute(VertexAttributeSrcP0)]]; + float2 srcP1 [[attribute(VertexAttributeSrcP1)]]; + float4 color [[attribute(VertexAttributeColor)]]; + int expandR [[attribute(VertexAttributeExpandR)]]; } Vertex; typedef struct { float4 position [[position]]; + float4 color; float2 texCoord; + int expandR; } ColorInOut; +constant float2 vertices[6] = { + {-1.0, -1.0}, + {+1.0, -1.0}, + {-1.0, +1.0}, + {+1.0, -1.0}, + {+1.0, +1.0}, + {-1.0, +1.0} +}; + vertex ColorInOut vertexShader(Vertex in [[stage_in]], - constant Uniforms & uniforms [[ buffer(BufferIndexUniforms) ]]) + constant Uniforms & uniforms [[ buffer(BufferIndexUniforms) ]], + uint vertex_id [[vertex_id]]) { ColorInOut out; - - float4 position = float4(in.position, 1.0); - out.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * position; - out.texCoord = in.texCoord; - + + float2 v = vertices[vertex_id]; + + float2 dstHalfSize = (in.dstP1 - in.dstP0) / 2.0; + float2 dstCenter = (in.dstP0 + in.dstP1) / 2.0; + float2 dstPos = v * dstHalfSize + dstCenter; + //dstPos.y = uniforms.resolution.y - dstPos.y; + out.position = float4(2.0 * (dstPos.x / uniforms.resolution.x) - 1.0, + 2.0 * (dstPos.y / uniforms.resolution.y) - 1.0, + 0.0, + 1.0); + out.color = in.color; + out.position.y *= -1.0; + + float2 srcHalfSize = (in.srcP1 - in.srcP0) / 2.0; + float2 srcCenter = (in.srcP0 + in.srcP1) / 2.0; + float2 srcPos = v * srcHalfSize + srcCenter; + out.texCoord = srcPos; + out.expandR = in.expandR; + return out; } fragment float4 fragmentShader(ColorInOut in [[stage_in]], constant Uniforms & uniforms [[ buffer(BufferIndexUniforms) ]], - texture2d colorMap [[ texture(TextureIndexColor) ]]) + texture2d colorMap [[ texture(TextureIndexColor) ]]) { constexpr sampler colorSampler(mip_filter::linear, mag_filter::linear, min_filter::linear); - half4 colorSample = colorMap.sample(colorSampler, in.texCoord.xy); + float4 colorSample = float4(colorMap.sample(colorSampler, in.texCoord.xy)) / 255.0 * in.color; - return float4(colorSample); + if (in.expandR == 1) + colorSample = colorSample.rrrr; + + return colorSample; }