Skip to content

Commit

Permalink
Create unified API for shaders
Browse files Browse the repository at this point in the history
  • Loading branch information
JoelOtter committed Feb 22, 2024
1 parent 819292f commit 9ad56e6
Show file tree
Hide file tree
Showing 11 changed files with 247 additions and 117 deletions.
4 changes: 4 additions & 0 deletions src/core/include/growl/core/api/graphics_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "../graphics/batch.h"
#include "../graphics/font_texture_atlas.h"
#include "../graphics/shader.h"
#include "../graphics/texture.h"
#include "../graphics/texture_atlas.h"
#include "growl/core/assets/font_face.h"
Expand Down Expand Up @@ -35,6 +36,9 @@ class GraphicsAPI {
virtual std::unique_ptr<Batch> createBatch() = 0;
virtual std::unique_ptr<Batch> createBatch(const Texture& texture) = 0;

virtual Result<std::unique_ptr<Shader>> createShader(
const std::string& vertex_src, const std::string& fragment_src) = 0;

protected:
double deltaTime = 0;
};
Expand Down
42 changes: 42 additions & 0 deletions src/core/include/growl/core/graphics/shader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#pragma once

#include "growl/core/error.h"
namespace Growl {

class Shader {
public:
Shader(const std::string& vertex_src, const std::string& fragment_src)
: vertex_src{vertex_src}
, fragment_src{fragment_src} {};
virtual ~Shader() = default;

// Shader is move-only
Shader(const Shader&) = delete;
Shader& operator=(const Shader&) = delete;
Shader(Shader&&) = default;
Shader& operator=(Shader&&) = default;

virtual Error compile() = 0;

virtual const std::string& getVertexSource() {
return vertex_src;
}

virtual const std::string& getFragmentSource() {
return fragment_src;
}

virtual void setVertexSource(const std::string& src) {
vertex_src = src;
}

virtual void setFragmentSource(const std::string& src) {
fragment_src = src;
}

protected:
std::string vertex_src;
std::string fragment_src;
};

} // namespace Growl
23 changes: 23 additions & 0 deletions src/plugins/metal/src/metal_error.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include "growl/core/error.h"
#include <Foundation/Foundation.h>
namespace Growl {

class MetalError : public BaseError {
public:
explicit MetalError(std::string message)
: message_str{message} {}

explicit MetalError(NSError* ns_err)
: message_str{[[ns_err localizedDescription] UTF8String]} {}

std::string message() override {
return message_str;
}

private:
std::string message_str;
};

} // namespace Growl
4 changes: 4 additions & 0 deletions src/plugins/metal/src/metal_graphics.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ class MetalGraphicsAPI : public GraphicsAPIInternal {
std::unique_ptr<Batch> createBatch() override;
std::unique_ptr<Batch> createBatch(const Texture& texture) override;

Result<std::unique_ptr<Shader>> createShader(
const std::string& vertex_src,
const std::string& fragment_src) override;

private:
API& api;
std::unique_ptr<Window> window;
Expand Down
38 changes: 33 additions & 5 deletions src/plugins/metal/src/metal_graphics.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
#include "glm/ext/matrix_clip_space.hpp"
#include "glm/mat4x4.hpp"
#include "growl/core/assets/font_face.h"
#include "growl/core/error.h"
#include "growl/core/graphics/shader.h"
#include "growl/core/imgui.h"
#include "metal_error.h"
#ifdef GROWL_IMGUI
#include "imgui_impl_metal.h"
#endif
Expand All @@ -15,7 +18,10 @@
using Growl::Batch;
using Growl::Error;
using Growl::FontTextureAtlas;
using Growl::MetalError;
using Growl::MetalGraphicsAPI;
using Growl::Result;
using Growl::Shader;
using Growl::Texture;
using Growl::TextureAtlas;
using Growl::TextureOptions;
Expand Down Expand Up @@ -121,11 +127,21 @@
}
vertex_buffers_ring = vertex_buffers;
command_queue = [device newCommandQueue];
default_shader =
std::make_unique<MetalShader>(device, MetalShader::DEFAULT_SHADER);
rect_shader =
std::make_unique<MetalShader>(device, MetalShader::RECT_SHADER);
sdf_shader = std::make_unique<MetalShader>(device, MetalShader::SDF_SHADER);
default_shader = std::make_unique<MetalShader>(
device, MetalShader::default_vertex, MetalShader::default_fragment);
if (auto err = default_shader->compile()) {
return err;
}
rect_shader = std::make_unique<MetalShader>(
device, MetalShader::default_vertex, MetalShader::rect_fragment);
if (auto err = rect_shader->compile()) {
return err;
}
sdf_shader = std::make_unique<MetalShader>(
device, MetalShader::default_vertex, MetalShader::sdf_fragment);
if (auto err = sdf_shader->compile()) {
return err;
}
return nullptr;
}

Expand Down Expand Up @@ -251,6 +267,18 @@
&vertex_buffer_offset);
}

Result<std::unique_ptr<Shader>> MetalGraphicsAPI::createShader(
const std::string& vertex_src, const std::string& fragment_src) {
auto shader =
std::make_unique<MetalShader>(device, vertex_src, fragment_src);
if (auto err = shader->compile()) {
return Error(std::make_unique<MetalError>(
"Failed to compile Metal shader: " + err->message()));
}
return std::unique_ptr<Shader>(
std::unique_ptr<MetalShader>(std::move(shader)));
}

const std::vector<unsigned char>
MetalGraphicsAPI::convertRGBAToBGRA(const Image& rgba) {
const unsigned char* src = rgba.getRaw();
Expand Down
21 changes: 15 additions & 6 deletions src/plugins/metal/src/metal_shader.h
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
#pragma once

#include "growl/core/graphics/shader.h"
#include <Metal/Metal.h>
#include <string>

namespace Growl {

class MetalShader {
class MetalShader : public Shader {
public:
explicit MetalShader(id<MTLDevice> device, NSString* const shader_src);
explicit MetalShader(
id<MTLDevice> device, const std::string& vertex_src,
const std::string& fragment_src)
: Shader(vertex_src, fragment_src)
, device{device} {}
~MetalShader();
void bind(id<MTLTexture> dst_texture, id<MTLRenderCommandEncoder> encoder);

static NSString* const DEFAULT_SHADER;
static NSString* const RECT_SHADER;
static NSString* const SDF_SHADER;
Error compile() override;

static const std::string default_vertex;
static const std::string default_fragment;
static const std::string sdf_fragment;
static const std::string rect_fragment;

private:
id<MTLDevice> device;
id<MTLFunction> vertex_func;
id<MTLFunction> fragment_func;
MTLRenderPipelineDescriptor* descriptor = nullptr;
MTLVertexDescriptor* vertex_descriptor;

static NSString* const GROWL_SHADER_HEADER;
static const std::string growl_shader_header;
};

} // namespace Growl
83 changes: 31 additions & 52 deletions src/plugins/metal/src/metal_shader.mm
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
#include "metal_shader.h"
#include "growl/core/error.h"
#include "metal_error.h"
#include <cassert>

using Growl::Error;
using Growl::MetalError;
using Growl::MetalShader;

MetalShader::MetalShader(id<MTLDevice> device, NSString* const shader_src) {
auto compile_options = [MTLCompileOptions new];
NSError* compile_error;
id<MTLLibrary> lib =
[device newLibraryWithSource:[GROWL_SHADER_HEADER
stringByAppendingString:shader_src]
options:compile_options
error:&compile_error];
assert(!compile_error);
fragment_func = [lib newFunctionWithName:@"pixel_func"];
vertex_func = [lib newFunctionWithName:@"vertex_func"];
[lib release];
[compile_error release];
[compile_options release];
}

MetalShader::~MetalShader() {
[vertex_func release];
[fragment_func release];
Expand All @@ -27,6 +15,27 @@
}
}

Error MetalShader::compile() {
@autoreleasepool {
auto compile_options = [[MTLCompileOptions new] autorelease];
NSError* compile_error = nil;
[compile_error autorelease];
std::string src = growl_shader_header + vertex_src + fragment_src;
id<MTLLibrary> lib = [[device
newLibraryWithSource:
[NSString stringWithCString:src.c_str()
encoding:[NSString defaultCStringEncoding]]
options:compile_options
error:&compile_error] autorelease];
if (compile_error) {
return std::make_unique<MetalError>(compile_error);
}
fragment_func = [lib newFunctionWithName:@"pixel_func"];
vertex_func = [lib newFunctionWithName:@"vertex_func"];
}
return nullptr;
}

void MetalShader::bind(
id<MTLTexture> dst_texture, id<MTLRenderCommandEncoder> encoder) {
if (!descriptor) {
Expand Down Expand Up @@ -63,7 +72,7 @@
[encoder setRenderPipelineState:pipeline_state];
}

NSString* const MetalShader::GROWL_SHADER_HEADER = @R"(
const std::string MetalShader::growl_shader_header = R"(
#include <metal_stdlib>
using namespace metal;
Expand All @@ -84,7 +93,7 @@
};
)";

NSString* const MetalShader::DEFAULT_SHADER = @R"(
const std::string MetalShader::default_vertex = R"(
vertex VertexOut vertex_func (
constant ConstantBlock& constant_block [[ buffer(0) ]],
const device VertexIn* vertex_array [[ buffer(1) ]],
Expand All @@ -100,7 +109,9 @@ vertex VertexOut vertex_func (
return outVertex;
}
)";

const std::string MetalShader::default_fragment = R"(
fragment float4 pixel_func (
VertexOut v [[ stage_in ]],
texture2d<float> tex0 [[ texture(0) ]],
Expand All @@ -110,47 +121,15 @@ fragment float4 pixel_func (
}
)";

NSString* const MetalShader::RECT_SHADER = @R"(
vertex VertexOut vertex_func (
constant ConstantBlock& constant_block [[ buffer(0) ]],
const device VertexIn* vertex_array [[ buffer(1) ]],
constant float4x4& transform [[ buffer(2) ]],
unsigned int vid [[ vertex_id ]]
) {
VertexIn v = vertex_array[vid];
VertexOut outVertex = VertexOut();
outVertex.texCoord0 = v.vertPos;
outVertex.position = constant_block.projection * transform * float4(v.position, 0, 1);
outVertex.color = v.color;
return outVertex;
}
const std::string MetalShader::rect_fragment = R"(
fragment float4 pixel_func (
VertexOut v [[ stage_in ]]
) {
return v.color;
}
)";

NSString* const MetalShader::SDF_SHADER = @R"(
vertex VertexOut vertex_func (
constant ConstantBlock& constant_block [[ buffer(0) ]],
const device VertexIn* vertex_array [[ buffer(1) ]],
constant float4x4& transform [[ buffer(2) ]],
unsigned int vid [[ vertex_id ]]
) {
VertexIn v = vertex_array[vid];
VertexOut outVertex = VertexOut();
outVertex.texCoord0 = v.vertPos;
outVertex.position = constant_block.projection * transform * float4(v.position, 0, 1);
outVertex.color = v.color;
return outVertex;
}
const std::string MetalShader::sdf_fragment = R"(
float median(float r, float g, float b) {
return max(min(r, g), min(max(r, g), b));
}
Expand Down
Loading

0 comments on commit 9ad56e6

Please sign in to comment.