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