1// 2// Copyright 2016 Pixar 3// 4// Licensed under the Apache License, Version 2.0 (the "Apache License") 5// with the following modification; you may not use this file except in 6// compliance with the Apache License and the following modification to it: 7// Section 6. Trademarks. is deleted and replaced with: 8// 9// 6. Trademarks. This License does not grant permission to use the trade 10// names, trademarks, service marks, or product names of the Licensor 11// and its affiliates, except as required to comply with Section 4(c) of 12// the License and to reproduce the content of the NOTICE file. 13// 14// You may obtain a copy of the Apache License at 15// 16// http://www.apache.org/licenses/LICENSE-2.0 17// 18// Unless required by applicable law or agreed to in writing, software 19// distributed under the Apache License with the above modification is 20// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 21// KIND, either express or implied. See the Apache License for the specific 22// language governing permissions and limitations under the Apache License. 23// 24 25#include "mtlHud.h" 26#include "font_image.h" 27#include "simple_math.h" 28#include <Metal/Metal.h> 29 30static const char* shaderSource = R"( 31#include <metal_stdlib> 32using namespace metal; 33 34struct VertexInput { 35 float2 position [[attribute(0)]]; 36 float3 color [[attribute(1)]]; 37 float2 uv [[attribute(2)]]; 38}; 39 40struct VertexOutput { 41 float4 position [[position]]; 42 float4 color; 43 float2 uv; 44}; 45 46 47 48vertex VertexOutput fg_vertex( 49 VertexInput in [[stage_in]], 50 constant const float4x4& ModelViewProjectionMatrix [[buffer(1)]], 51 constant const float& UIScale [[buffer(2)]] 52 ) { 53 VertexOutput out; 54 55 out.position = ModelViewProjectionMatrix * float4(in.position * UIScale, 0, 1); 56 out.color = float4(in.color, 1); 57 out.uv = in.uv; 58 59 return out; 60} 61 62fragment float4 fg_fragment( 63 VertexOutput in [[stage_in]], 64 texture2d<float, access::sample> fontTexture [[texture(0)]] 65 ) { 66 67 constexpr sampler s = sampler(coord::normalized, address::clamp_to_zero, filter::nearest); 68 auto c = fontTexture.sample(s, in.uv); 69 if(c.a == 0) 70 discard_fragment(); 71 72 return c * in.color; 73} 74 75constant float4 bgVertices[] = { 76 {-1, 1, 0, 1}, 77 {1, 1, 0, 1}, 78 {-1, -1, 0, 1}, 79 {1, -1, 0, 1} 80}; 81 82vertex VertexOutput bg_vertex(uint vertex_id [[vertex_id]]) { 83 VertexOutput out; 84 85 out.position = bgVertices[vertex_id]; 86 out.uv = bgVertices[vertex_id].xy * 0.5 + 0.5 * 3.14159; 87 88 return out; 89} 90 91fragment float4 bg_fragment(VertexOutput in [[stage_in]]) { 92 return float4(float3(mix(0.1, 0.5, sin(in.uv.y))), 1); 93} 94 95)"; 96 97 98MTLhud::MTLhud() { 99 100 101} 102 103MTLhud::~MTLhud() { 104 105} 106 107void 108MTLhud::Init(id<MTLDevice> device, MTLRenderPipelineDescriptor* parentPipelineDescriptor, MTLDepthStencilDescriptor* depthStencilStateDescriptor, int width, int height, int framebufferWidth, int framebufferHeight) { 109 Hud::Init(width, height, framebufferWidth, framebufferHeight); 110 111 @autoreleasepool { 112 _device = device; 113 const auto textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm width:FONT_TEXTURE_WIDTH height:FONT_TEXTURE_HEIGHT mipmapped:false]; 114 _fontTexture = [_device newTextureWithDescriptor:textureDescriptor]; 115 [_fontTexture replaceRegion: { {0, 0, 0}, {FONT_TEXTURE_WIDTH, FONT_TEXTURE_HEIGHT, 1} } mipmapLevel:0 withBytes:font_image bytesPerRow:4 * FONT_TEXTURE_WIDTH]; 116 117 NSError* err = nil; 118 const auto library = [_device newLibraryWithSource:@(shaderSource) options:nil error:&err]; 119 assert(err == nil); 120 121 const auto renderPipelineDescriptor = (MTLRenderPipelineDescriptor*)[parentPipelineDescriptor copy]; 122 renderPipelineDescriptor.vertexFunction = [library newFunctionWithName:@"bg_vertex"]; 123 renderPipelineDescriptor.fragmentFunction = [library newFunctionWithName:@"bg_fragment"]; 124 125 _bgPipelineState = [_device newRenderPipelineStateWithDescriptor:renderPipelineDescriptor error:&err]; 126 127 renderPipelineDescriptor.vertexFunction = [library newFunctionWithName:@"fg_vertex"]; 128 renderPipelineDescriptor.fragmentFunction = [library newFunctionWithName:@"fg_fragment"]; 129 const auto vertexDescriptor = renderPipelineDescriptor.vertexDescriptor; 130 131 vertexDescriptor.layouts[0].stride = sizeof(float) * 7; 132 vertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex; 133 vertexDescriptor.layouts[0].stepRate = 1; 134 135 vertexDescriptor.attributes[0].offset = 0; 136 vertexDescriptor.attributes[0].format = MTLVertexFormatFloat2; 137 vertexDescriptor.attributes[0].bufferIndex = 0; 138 139 vertexDescriptor.attributes[1].offset = sizeof(float) * 2; 140 vertexDescriptor.attributes[1].format = MTLVertexFormatFloat3; 141 vertexDescriptor.attributes[1].bufferIndex = 0; 142 143 vertexDescriptor.attributes[2].offset = sizeof(float) * 5; 144 vertexDescriptor.attributes[2].format = MTLVertexFormatFloat2; 145 vertexDescriptor.attributes[2].bufferIndex = 0; 146 147 _fgPipelineState = [_device newRenderPipelineStateWithDescriptor:renderPipelineDescriptor error:&err]; 148 149 _depthStencilState = [_device newDepthStencilStateWithDescriptor:depthStencilStateDescriptor]; 150 } 151 152} 153 154void 155MTLhud::Rebuild(int width, int height, int framebufferWidth, int framebufferHeight) { 156 Hud::Rebuild(width, height, framebufferWidth, framebufferHeight); 157 158 _staticBuffer.alloc(_device, getStaticVboSource().size(), @"MTLhud static buffer"); 159 std::copy(getStaticVboSource().begin(), getStaticVboSource().end(), _staticBuffer.data()); 160 _staticBuffer.markModified(); 161} 162 163bool 164MTLhud::Flush(id<MTLRenderCommandEncoder> encoder) { 165 if(!Hud::Flush()) 166 return false; 167 168 if(_dynamicBuffer.buffer().length < sizeof(float) * getVboSource().size()) { 169 _dynamicBuffer.alloc(_device, getVboSource().size(), @"MTLhud dynamic buffer"); 170 } else { 171 _dynamicBuffer.next(); 172 } 173 174 std::copy(getVboSource().begin(), getVboSource().end(), _dynamicBuffer.data()); 175 _dynamicBuffer.markModified(); 176 const auto numVertices = getVboSource().size() / 7; 177 getVboSource().clear(); 178 179 float proj[16]; 180 ortho(proj, 0, 0, float(GetWidth()), float(GetHeight())); 181 182 [encoder setVertexBuffer: _dynamicBuffer.buffer() offset:0 atIndex:0]; 183 [encoder setRenderPipelineState: _fgPipelineState]; 184 [encoder setDepthStencilState: _depthStencilState]; 185 [encoder setVertexBytes: proj length: sizeof(proj) atIndex:1]; 186 [encoder setVertexBytes: &UIScale length:sizeof(UIScale) atIndex:2]; 187 [encoder setFragmentTexture: _fontTexture atIndex:0]; 188 189 if(numVertices > 0) { 190 [encoder setVertexBuffer:_dynamicBuffer.buffer() offset:0 atIndex:0]; 191 [encoder drawPrimitives: MTLPrimitiveTypeTriangle vertexStart:0 vertexCount: numVertices]; 192 } 193 194 auto numStaticVertices = [_staticBuffer.buffer() length] / (7 * sizeof(float)); 195 if(numStaticVertices > 0) { 196 [encoder setVertexBuffer:_staticBuffer.buffer() offset:0 atIndex:0]; 197 [encoder drawPrimitives: MTLPrimitiveTypeTriangle vertexStart:0 vertexCount: numStaticVertices]; 198 } 199 200 return true; 201} 202 203void 204MTLhud::FillBackground(id<MTLRenderCommandEncoder> encoder) { 205 [encoder setRenderPipelineState: _bgPipelineState]; 206 [encoder setDepthStencilState: _depthStencilState]; 207 [encoder drawPrimitives: MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount: 4]; 208} 209