Metal: MTLState module implementation.

MTLState module implementation and supporting functionality in MTLContext for state tracking, texture binding and sampler state caching.

Ref T96261

Reviewed By: fclem

Maniphest Tasks: T96261

Differential Revision: https://developer.blender.org/D14827
This commit is contained in:
Jason Fielder 2022-05-12 20:49:09 +02:00 committed by Clément Foucault
parent 992ae3f282
commit 073139e329
Notes: blender-bot 2023-02-14 08:39:23 +01:00
Referenced by issue #96261, Metal Viewport
8 changed files with 1319 additions and 47 deletions

View File

@ -85,6 +85,7 @@ set(SRC
GPU_buffers.h
GPU_capabilities.h
GPU_common.h
GPU_common_types.h
GPU_compute.h
GPU_context.h
GPU_debug.h
@ -189,6 +190,7 @@ set(METAL_SRC
metal/mtl_backend.mm
metal/mtl_context.mm
metal/mtl_debug.mm
metal/mtl_state.mm
metal/mtl_texture.mm
metal/mtl_texture_util.mm
@ -197,6 +199,7 @@ set(METAL_SRC
metal/mtl_common.hh
metal/mtl_context.hh
metal/mtl_debug.hh
metal/mtl_state.hh
metal/mtl_texture.hh
)

View File

@ -0,0 +1,18 @@
/** \file
* \ingroup gpu
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef enum eGPUFrontFace {
GPU_CLOCKWISE,
GPU_COUNTERCLOCKWISE,
} eGPUFrontFace;
#ifdef __cplusplus
}
#endif

View File

@ -35,6 +35,18 @@ typedef enum eGPUBarrier {
ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_ELEMENT_ARRAY)
/* Note: For Metal and Vulkan only. TODO(Metal): Update barrier calls to use stage flags. */
typedef enum eGPUStageBarrierBits {
GPU_BARRIER_STAGE_VERTEX = (1 << 0),
GPU_BARRIER_STAGE_FRAGMENT = (1 << 1),
GPU_BARRIER_STAGE_COMPUTE = (1 << 2),
GPU_BARRIER_STAGE_ANY_GRAPHICS = (GPU_BARRIER_STAGE_VERTEX | GPU_BARRIER_STAGE_FRAGMENT),
GPU_BARRIER_STAGE_ANY = (GPU_BARRIER_STAGE_VERTEX | GPU_BARRIER_STAGE_FRAGMENT |
GPU_BARRIER_STAGE_COMPUTE),
} eGPUStageBarrierBits;
ENUM_OPERATORS(eGPUStageBarrierBits, GPU_BARRIER_STAGE_COMPUTE)
/**
* Defines the fixed pipeline blending equation.
* SRC is the output color from the shader.

View File

@ -7,8 +7,10 @@
#include "gpu_context_private.hh"
#include "GPU_common_types.h"
#include "GPU_context.h"
#include "mtl_capabilities.hh"
#include "mtl_texture.hh"
#include <Cocoa/Cocoa.h>
@ -21,6 +23,112 @@
namespace blender::gpu {
class MTLShader;
class MTLUniformBuf;
class MTLBuffer;
/* Depth Stencil State */
typedef struct MTLContextDepthStencilState {
/* Depth State. */
bool depth_write_enable;
bool depth_test_enabled;
float depth_range_near;
float depth_range_far;
MTLCompareFunction depth_function;
float depth_bias;
float depth_slope_scale;
bool depth_bias_enabled_for_points;
bool depth_bias_enabled_for_lines;
bool depth_bias_enabled_for_tris;
/* Stencil State. */
bool stencil_test_enabled;
unsigned int stencil_read_mask;
unsigned int stencil_write_mask;
unsigned int stencil_ref;
MTLCompareFunction stencil_func;
MTLStencilOperation stencil_op_front_stencil_fail;
MTLStencilOperation stencil_op_front_depth_fail;
MTLStencilOperation stencil_op_front_depthstencil_pass;
MTLStencilOperation stencil_op_back_stencil_fail;
MTLStencilOperation stencil_op_back_depth_fail;
MTLStencilOperation stencil_op_back_depthstencil_pass;
/* Framebuffer State -- We need to mark this, incase stencil state remains unchanged,
* but attachment state has changed. */
bool has_depth_target;
bool has_stencil_target;
/* TODO(Metal): Consider optimising this function using memcmp.
* Un-used, but differing, stencil state leads to over-generation
* of state objects when doing trivial compare. */
inline bool operator==(const MTLContextDepthStencilState &other) const
{
bool depth_state_equality = (has_depth_target == other.has_depth_target &&
depth_write_enable == other.depth_write_enable &&
depth_test_enabled == other.depth_test_enabled &&
depth_function == other.depth_function);
bool stencil_state_equality = true;
if (has_stencil_target) {
stencil_state_equality =
(has_stencil_target == other.has_stencil_target &&
stencil_test_enabled == other.stencil_test_enabled &&
stencil_op_front_stencil_fail == other.stencil_op_front_stencil_fail &&
stencil_op_front_depth_fail == other.stencil_op_front_depth_fail &&
stencil_op_front_depthstencil_pass == other.stencil_op_front_depthstencil_pass &&
stencil_op_back_stencil_fail == other.stencil_op_back_stencil_fail &&
stencil_op_back_depth_fail == other.stencil_op_back_depth_fail &&
stencil_op_back_depthstencil_pass == other.stencil_op_back_depthstencil_pass &&
stencil_func == other.stencil_func && stencil_read_mask == other.stencil_read_mask &&
stencil_write_mask == other.stencil_write_mask);
}
return depth_state_equality && stencil_state_equality;
}
/* Depth stencil state will get hashed in order to prepare
* MTLDepthStencilState objects. The hash should comprise of
* all elements which fill the MTLDepthStencilDescriptor.
* These are bound when [rec setDepthStencilState:...] is called.
* Depth bias and stencil reference value are set dynamically on the RenderCommandEncoder:
* - setStencilReferenceValue:
* - setDepthBias:slopeScale:clamp:
*/
inline std::size_t hash() const
{
std::size_t boolean_bitmask = (this->depth_write_enable ? 1 : 0) |
((this->depth_test_enabled ? 1 : 0) << 1) |
((this->depth_bias_enabled_for_points ? 1 : 0) << 2) |
((this->depth_bias_enabled_for_lines ? 1 : 0) << 3) |
((this->depth_bias_enabled_for_tris ? 1 : 0) << 4) |
((this->stencil_test_enabled ? 1 : 0) << 5) |
((this->has_depth_target ? 1 : 0) << 6) |
((this->has_stencil_target ? 1 : 0) << 7);
std::size_t stencilop_bitmask = ((std::size_t)this->stencil_op_front_stencil_fail) |
((std::size_t)this->stencil_op_front_depth_fail << 3) |
((std::size_t)this->stencil_op_front_depthstencil_pass << 6) |
((std::size_t)this->stencil_op_back_stencil_fail << 9) |
((std::size_t)this->stencil_op_back_depth_fail << 12) |
((std::size_t)this->stencil_op_back_depthstencil_pass << 15);
std::size_t main_hash = (std::size_t)this->depth_function;
if (this->has_stencil_target) {
main_hash += (std::size_t)(this->stencil_read_mask & 0xFF) << 8;
main_hash += (std::size_t)(this->stencil_write_mask & 0xFF) << 16;
}
main_hash ^= (std::size_t)this->stencil_func << 16;
main_hash ^= stencilop_bitmask;
std::size_t final_hash = (main_hash << 8) | boolean_bitmask;
return final_hash;
}
} MTLContextDepthStencilState;
typedef struct MTLContextTextureUtils {
/* Depth Update Utilities */
@ -108,11 +216,149 @@ typedef struct MTLContextTextureUtils {
} MTLContextTextureUtils;
typedef struct MTLContextGlobalShaderPipelineState {
/* ..TODO(Metal): More elements to be added as backend fleshed out.. */
/* Structs containing information on current binding state for textures and samplers. */
typedef struct MTLTextureBinding {
bool used;
/*** DATA and IMAGE access state ***/
/* Same value as index in bindings array. */
unsigned int texture_slot_index;
gpu::MTLTexture *texture_resource;
} MTLTextureBinding;
typedef struct MTLSamplerBinding {
bool used;
MTLSamplerState state;
bool operator==(MTLSamplerBinding const &other) const
{
return (used == other.used && state == other.state);
}
} MTLSamplerBinding;
/* Combined sampler state configuration for Argument Buffer caching. */
struct MTLSamplerArray {
unsigned int num_samplers;
/* MTLSamplerState permutations between 0..256 - slightly more than a byte. */
MTLSamplerState mtl_sampler_flags[MTL_MAX_TEXTURE_SLOTS];
id<MTLSamplerState> mtl_sampler[MTL_MAX_TEXTURE_SLOTS];
inline bool operator==(const MTLSamplerArray &other) const
{
if (this->num_samplers != other.num_samplers) {
return false;
}
return (memcmp(this->mtl_sampler_flags,
other.mtl_sampler_flags,
sizeof(MTLSamplerState) * this->num_samplers) == 0);
}
inline uint32_t hash() const
{
uint32_t hash = this->num_samplers;
for (int i = 0; i < this->num_samplers; i++) {
hash ^= (uint32_t)this->mtl_sampler_flags[i] << (i % 3);
}
return hash;
}
};
typedef enum MTLPipelineStateDirtyFlag {
MTL_PIPELINE_STATE_NULL_FLAG = 0,
/* Whether we need to call setViewport. */
MTL_PIPELINE_STATE_VIEWPORT_FLAG = (1 << 0),
/* Whether we need to call setScissor.*/
MTL_PIPELINE_STATE_SCISSOR_FLAG = (1 << 1),
/* Whether we need to update/rebind active depth stencil state. */
MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG = (1 << 2),
/* Whether we need to update/rebind active PSO. */
MTL_PIPELINE_STATE_PSO_FLAG = (1 << 3),
/* Whether we need to update the frontFacingWinding state. */
MTL_PIPELINE_STATE_FRONT_FACING_FLAG = (1 << 4),
/* Whether we need to update the culling state. */
MTL_PIPELINE_STATE_CULLMODE_FLAG = (1 << 5),
/* Full pipeline state needs applying. Occurs when beginning a new render pass. */
MTL_PIPELINE_STATE_ALL_FLAG =
(MTL_PIPELINE_STATE_VIEWPORT_FLAG | MTL_PIPELINE_STATE_SCISSOR_FLAG |
MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG | MTL_PIPELINE_STATE_PSO_FLAG |
MTL_PIPELINE_STATE_FRONT_FACING_FLAG | MTL_PIPELINE_STATE_CULLMODE_FLAG)
} MTLPipelineStateDirtyFlag;
/* Ignore full flag bit-mask `MTL_PIPELINE_STATE_ALL_FLAG`. */
ENUM_OPERATORS(MTLPipelineStateDirtyFlag, MTL_PIPELINE_STATE_CULLMODE_FLAG);
typedef struct MTLUniformBufferBinding {
bool bound;
MTLUniformBuf *ubo;
} MTLUniformBufferBinding;
typedef struct MTLContextGlobalShaderPipelineState {
bool initialised;
/* Whether the pipeline state has been modified since application.
* `dirty_flags` is a bitmask of the types of state which have been updated.
* This is in order to optimise calls and only re-apply state as needed.
* Some state parameters are dynamically applied on the RenderCommandEncoder,
* others may be encapsulated in GPU-resident state objects such as
* MTLDepthStencilState or MTLRenderPipelineState. */
bool dirty;
MTLPipelineStateDirtyFlag dirty_flags;
/* Shader resources. */
MTLShader *null_shader;
/* Active Shader State. */
MTLShader *active_shader;
/* Global Uniform Buffers. */
MTLUniformBufferBinding ubo_bindings[MTL_MAX_UNIFORM_BUFFER_BINDINGS];
/* Context Texture bindings. */
MTLTextureBinding texture_bindings[MTL_MAX_TEXTURE_SLOTS];
MTLSamplerBinding sampler_bindings[MTL_MAX_SAMPLER_SLOTS];
/*** --- Render Pipeline State --- ***/
/* Track global render pipeline state for the current context. The functions in GPU_state.h
* modify these parameters. Certain values, tagged [PSO], are parameters which are required to be
* passed into PSO creation, rather than dynamic state functions on the RenderCommandEncoder.
*/
/* Blending State. */
MTLColorWriteMask color_write_mask; /* [PSO] */
bool blending_enabled; /* [PSO] */
MTLBlendOperation alpha_blend_op; /* [PSO] */
MTLBlendOperation rgb_blend_op; /* [PSO] */
MTLBlendFactor dest_alpha_blend_factor; /* [PSO] */
MTLBlendFactor dest_rgb_blend_factor; /* [PSO] */
MTLBlendFactor src_alpha_blend_factor; /* [PSO] */
MTLBlendFactor src_rgb_blend_factor; /* [PSO] */
/* Culling State. */
bool culling_enabled;
eGPUFaceCullTest cull_mode;
eGPUFrontFace front_face;
/* Depth State. */
MTLContextDepthStencilState depth_stencil_state;
/* Viewport/Scissor Region. */
int viewport_offset_x;
int viewport_offset_y;
int viewport_width;
int viewport_height;
bool scissor_enabled;
int scissor_x;
int scissor_y;
int scissor_width;
int scissor_height;
/* Image data access state. */
uint unpack_row_length;
/* Render parameters. */
float point_size = 1.0f;
float line_width = 1.0f;
} MTLContextGlobalShaderPipelineState;
/* Metal Buffer */
@ -127,7 +373,7 @@ typedef struct MTLTemporaryBufferRange {
bool requires_flush();
} MTLTemporaryBufferRange;
/** MTLContext -- Core render loop and state management **/
/** MTLContext -- Core render loop and state management. **/
/* Note(Metal): Partial MTLContext stub to provide wrapper functionality
* for work-in-progress MTL* classes. */
@ -138,8 +384,24 @@ class MTLContext : public Context {
/* Compute and specialization caches. */
MTLContextTextureUtils texture_utils_;
/* Texture Samplers. */
/* Cache of generated MTLSamplerState objects based on permutations of `eGPUSamplerState`. */
id<MTLSamplerState> sampler_state_cache_[GPU_SAMPLER_MAX] = {0};
id<MTLSamplerState> default_sampler_state_ = nil;
/* When texture sampler count exceeds the resource bind limit, an
* argument buffer is used to pass samplers to the shader.
* Each unique configurations of multiple samplers can be cached, so as to not require
* re-generation. `samplers_` stores the current list of bound sampler objects.
* `cached_sampler_buffers_` is a cache of encoded argument buffers which can be re-used. */
MTLSamplerArray samplers_;
blender::Map<MTLSamplerArray, gpu::MTLBuffer *> cached_sampler_buffers_;
public:
/* METAL API Resource Handles. */
/* Shaders and Pipeline state. */
MTLContextGlobalShaderPipelineState pipeline_state;
/* Metal API Resource Handles. */
id<MTLCommandQueue> queue = nil;
id<MTLDevice> device = nil;
@ -160,24 +422,40 @@ class MTLContext : public Context {
void debug_group_begin(const char *name, int index) override;
void debug_group_end(void) override;
/*** Context Utility functions */
/*** MTLContext Utility functions. */
/*
* All below functions modify the global state for the context, controlling the flow of
* rendering, binding resources, setting global state, resource management etc;
*/
/* Metal Context Core functions */
/* Command Buffer Management */
/* Metal Context Core functions. */
/* Command Buffer Management. */
id<MTLCommandBuffer> get_active_command_buffer();
/* Render Pass State and Management */
/* Render Pass State and Management. */
void begin_render_pass();
void end_render_pass();
bool is_render_pass_active();
/* Shaders and Pipeline state */
MTLContextGlobalShaderPipelineState pipeline_state;
/* Texture Binding. */
void texture_bind(gpu::MTLTexture *mtl_texture, unsigned int texture_unit);
void sampler_bind(MTLSamplerState, unsigned int sampler_unit);
void texture_unbind(gpu::MTLTexture *mtl_texture);
void texture_unbind_all(void);
id<MTLSamplerState> get_sampler_from_state(MTLSamplerState state);
id<MTLSamplerState> generate_sampler_from_state(MTLSamplerState state);
id<MTLSamplerState> get_default_sampler_state();
/* Texture utilities */
/* Metal Context pipeline state. */
void pipeline_state_init(void);
MTLShader *get_active_shader(void);
/* State assignment. */
void set_viewport(int origin_x, int origin_y, int width, int height);
void set_scissor(int scissor_x, int scissor_y, int scissor_width, int scissor_height);
void set_scissor_enabled(bool scissor_enabled);
/* Texture utilities. */
MTLContextTextureUtils &get_texture_utils()
{
return this->texture_utils_;

View File

@ -5,6 +5,11 @@
*/
#include "mtl_context.hh"
#include "mtl_debug.hh"
#include "mtl_state.hh"
#include "DNA_userdef_types.h"
#include "GPU_capabilities.h"
using namespace blender;
using namespace blender::gpu;
@ -44,6 +49,9 @@ MTLContext::MTLContext(void *ghost_window)
/* Init debug. */
debug::mtl_debug_init();
/* Initialise Metal modules. */
this->state_manager = new MTLStateManager(this);
/* TODO(Metal): Implement. */
}
@ -98,6 +106,234 @@ void MTLContext::end_render_pass()
/* TODO(Metal): Implement. */
}
bool MTLContext::is_render_pass_active()
{
/* TODO(Metal): Implement. */
return false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Global Context State
* \{ */
/* Metal Context Pipeline State. */
void MTLContext::pipeline_state_init()
{
/*** Initialise state only once. ***/
if (!this->pipeline_state.initialised) {
this->pipeline_state.initialised = true;
this->pipeline_state.active_shader = NULL;
/* Clear bindings state. */
for (int t = 0; t < GPU_max_textures(); t++) {
this->pipeline_state.texture_bindings[t].used = false;
this->pipeline_state.texture_bindings[t].texture_slot_index = t;
this->pipeline_state.texture_bindings[t].texture_resource = NULL;
}
for (int s = 0; s < MTL_MAX_SAMPLER_SLOTS; s++) {
this->pipeline_state.sampler_bindings[s].used = false;
}
for (int u = 0; u < MTL_MAX_UNIFORM_BUFFER_BINDINGS; u++) {
this->pipeline_state.ubo_bindings[u].bound = false;
this->pipeline_state.ubo_bindings[u].ubo = NULL;
}
}
/*** State defaults -- restored by GPU_state_init. ***/
/* Clear blending State. */
this->pipeline_state.color_write_mask = MTLColorWriteMaskRed | MTLColorWriteMaskGreen |
MTLColorWriteMaskBlue | MTLColorWriteMaskAlpha;
this->pipeline_state.blending_enabled = false;
this->pipeline_state.alpha_blend_op = MTLBlendOperationAdd;
this->pipeline_state.rgb_blend_op = MTLBlendOperationAdd;
this->pipeline_state.dest_alpha_blend_factor = MTLBlendFactorZero;
this->pipeline_state.dest_rgb_blend_factor = MTLBlendFactorZero;
this->pipeline_state.src_alpha_blend_factor = MTLBlendFactorOne;
this->pipeline_state.src_rgb_blend_factor = MTLBlendFactorOne;
/* Viewport and scissor. */
this->pipeline_state.viewport_offset_x = 0;
this->pipeline_state.viewport_offset_y = 0;
this->pipeline_state.viewport_width = 0;
this->pipeline_state.viewport_height = 0;
this->pipeline_state.scissor_x = 0;
this->pipeline_state.scissor_y = 0;
this->pipeline_state.scissor_width = 0;
this->pipeline_state.scissor_height = 0;
this->pipeline_state.scissor_enabled = false;
/* Culling State. */
this->pipeline_state.culling_enabled = false;
this->pipeline_state.cull_mode = GPU_CULL_NONE;
this->pipeline_state.front_face = GPU_COUNTERCLOCKWISE;
/* DATA and IMAGE access state. */
this->pipeline_state.unpack_row_length = 0;
/* Depth State. */
this->pipeline_state.depth_stencil_state.depth_write_enable = false;
this->pipeline_state.depth_stencil_state.depth_test_enabled = false;
this->pipeline_state.depth_stencil_state.depth_range_near = 0.0;
this->pipeline_state.depth_stencil_state.depth_range_far = 1.0;
this->pipeline_state.depth_stencil_state.depth_function = MTLCompareFunctionAlways;
this->pipeline_state.depth_stencil_state.depth_bias = 0.0;
this->pipeline_state.depth_stencil_state.depth_slope_scale = 0.0;
this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_points = false;
this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_lines = false;
this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_tris = false;
/* Stencil State. */
this->pipeline_state.depth_stencil_state.stencil_test_enabled = false;
this->pipeline_state.depth_stencil_state.stencil_read_mask = 0xFF;
this->pipeline_state.depth_stencil_state.stencil_write_mask = 0xFF;
this->pipeline_state.depth_stencil_state.stencil_ref = 0;
this->pipeline_state.depth_stencil_state.stencil_func = MTLCompareFunctionAlways;
this->pipeline_state.depth_stencil_state.stencil_op_front_stencil_fail = MTLStencilOperationKeep;
this->pipeline_state.depth_stencil_state.stencil_op_front_depth_fail = MTLStencilOperationKeep;
this->pipeline_state.depth_stencil_state.stencil_op_front_depthstencil_pass =
MTLStencilOperationKeep;
this->pipeline_state.depth_stencil_state.stencil_op_back_stencil_fail = MTLStencilOperationKeep;
this->pipeline_state.depth_stencil_state.stencil_op_back_depth_fail = MTLStencilOperationKeep;
this->pipeline_state.depth_stencil_state.stencil_op_back_depthstencil_pass =
MTLStencilOperationKeep;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Texture State Management
* \{ */
void MTLContext::texture_bind(gpu::MTLTexture *mtl_texture, unsigned int texture_unit)
{
BLI_assert(this);
BLI_assert(mtl_texture);
if (texture_unit < 0 || texture_unit >= GPU_max_textures() ||
texture_unit >= MTL_MAX_TEXTURE_SLOTS) {
MTL_LOG_WARNING("Attempting to bind texture '%s' to invalid texture unit %d\n",
mtl_texture->get_name(),
texture_unit);
BLI_assert(false);
return;
}
/* Bind new texture. */
this->pipeline_state.texture_bindings[texture_unit].texture_resource = mtl_texture;
this->pipeline_state.texture_bindings[texture_unit].used = true;
mtl_texture->is_bound_ = true;
}
void MTLContext::sampler_bind(MTLSamplerState sampler_state, unsigned int sampler_unit)
{
BLI_assert(this);
if (sampler_unit < 0 || sampler_unit >= GPU_max_textures() ||
sampler_unit >= MTL_MAX_SAMPLER_SLOTS) {
MTL_LOG_WARNING("Attempting to bind sampler to invalid sampler unit %d\n", sampler_unit);
BLI_assert(false);
return;
}
/* Apply binding. */
this->pipeline_state.sampler_bindings[sampler_unit] = {true, sampler_state};
}
void MTLContext::texture_unbind(gpu::MTLTexture *mtl_texture)
{
BLI_assert(mtl_texture);
/* Iterate through textures in state and unbind. */
for (int i = 0; i < min_uu(GPU_max_textures(), MTL_MAX_TEXTURE_SLOTS); i++) {
if (this->pipeline_state.texture_bindings[i].texture_resource == mtl_texture) {
this->pipeline_state.texture_bindings[i].texture_resource = nullptr;
this->pipeline_state.texture_bindings[i].used = false;
}
}
/* Locally unbind texture. */
mtl_texture->is_bound_ = false;
}
void MTLContext::texture_unbind_all()
{
/* Iterate through context's bound textures. */
for (int t = 0; t < min_uu(GPU_max_textures(), MTL_MAX_TEXTURE_SLOTS); t++) {
if (this->pipeline_state.texture_bindings[t].used &&
this->pipeline_state.texture_bindings[t].texture_resource) {
this->pipeline_state.texture_bindings[t].used = false;
this->pipeline_state.texture_bindings[t].texture_resource = nullptr;
}
}
}
id<MTLSamplerState> MTLContext::get_sampler_from_state(MTLSamplerState sampler_state)
{
BLI_assert((unsigned int)sampler_state >= 0 && ((unsigned int)sampler_state) < GPU_SAMPLER_MAX);
return this->sampler_state_cache_[(unsigned int)sampler_state];
}
id<MTLSamplerState> MTLContext::generate_sampler_from_state(MTLSamplerState sampler_state)
{
/* Check if samper already exists for given state. */
id<MTLSamplerState> st = this->sampler_state_cache_[(unsigned int)sampler_state];
if (st != nil) {
return st;
}
else {
MTLSamplerDescriptor *descriptor = [[MTLSamplerDescriptor alloc] init];
descriptor.normalizedCoordinates = true;
MTLSamplerAddressMode clamp_type = (sampler_state.state & GPU_SAMPLER_CLAMP_BORDER) ?
MTLSamplerAddressModeClampToBorderColor :
MTLSamplerAddressModeClampToEdge;
descriptor.rAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_R) ?
MTLSamplerAddressModeRepeat :
clamp_type;
descriptor.sAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_S) ?
MTLSamplerAddressModeRepeat :
clamp_type;
descriptor.tAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_T) ?
MTLSamplerAddressModeRepeat :
clamp_type;
descriptor.borderColor = MTLSamplerBorderColorTransparentBlack;
descriptor.minFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ?
MTLSamplerMinMagFilterLinear :
MTLSamplerMinMagFilterNearest;
descriptor.magFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ?
MTLSamplerMinMagFilterLinear :
MTLSamplerMinMagFilterNearest;
descriptor.mipFilter = (sampler_state.state & GPU_SAMPLER_MIPMAP) ?
MTLSamplerMipFilterLinear :
MTLSamplerMipFilterNotMipmapped;
descriptor.lodMinClamp = -1000;
descriptor.lodMaxClamp = 1000;
float aniso_filter = max_ff(16, U.anisotropic_filter);
descriptor.maxAnisotropy = (sampler_state.state & GPU_SAMPLER_MIPMAP) ? aniso_filter : 1;
descriptor.compareFunction = (sampler_state.state & GPU_SAMPLER_COMPARE) ?
MTLCompareFunctionLessEqual :
MTLCompareFunctionAlways;
descriptor.supportArgumentBuffers = true;
id<MTLSamplerState> state = [this->device newSamplerStateWithDescriptor:descriptor];
this->sampler_state_cache_[(unsigned int)sampler_state] = state;
BLI_assert(state != nil);
[descriptor autorelease];
return state;
}
}
id<MTLSamplerState> MTLContext::get_default_sampler_state()
{
if (this->default_sampler_state_ == nil) {
this->default_sampler_state_ = this->get_sampler_from_state(DEFAULT_SAMPLER_STATE);
}
return this->default_sampler_state_;
}
/** \} */
} // blender::gpu

View File

@ -0,0 +1,73 @@
/** \file
* \ingroup gpu
*/
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "GPU_state.h"
#include "gpu_state_private.hh"
namespace blender::gpu {
/* Forward Declarations. */
class MTLContext;
/**
* State manager keeping track of the draw state and applying it before drawing.
* Metal Implementation.
**/
class MTLStateManager : public StateManager {
public:
private:
/* Current state of the associated MTLContext.
* Avoids resetting the whole state for every change. */
GPUState current_;
GPUStateMutable current_mutable_;
MTLContext *context_;
public:
MTLStateManager(MTLContext *ctx);
void apply_state(void) override;
void force_state(void) override;
void issue_barrier(eGPUBarrier barrier_bits) override;
void texture_bind(Texture *tex, eGPUSamplerState sampler, int unit) override;
void texture_unbind(Texture *tex) override;
void texture_unbind_all(void) override;
void image_bind(Texture *tex, int unit) override;
void image_unbind(Texture *tex) override;
void image_unbind_all(void) override;
void texture_unpack_row_length_set(uint len) override;
private:
void set_write_mask(const eGPUWriteMask value);
void set_depth_test(const eGPUDepthTest value);
void set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation);
void set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state);
void set_clip_distances(const int new_dist_len, const int old_dist_len);
void set_logic_op(const bool enable);
void set_facing(const bool invert);
void set_backface_culling(const eGPUFaceCullTest test);
void set_provoking_vert(const eGPUProvokingVertex vert);
void set_shadow_bias(const bool enable);
void set_blend(const eGPUBlend value);
void set_state(const GPUState &state);
void set_mutable_state(const GPUStateMutable &state);
/* METAL State utility functions. */
void mtl_state_init(void);
void mtl_depth_range(float near, float far);
void mtl_stencil_mask(unsigned int mask);
void mtl_stencil_set_func(eGPUStencilTest stencil_func, int ref, unsigned int mask);
MEM_CXX_CLASS_ALLOC_FUNCS("MTLStateManager")
};
} // namespace blender::gpu

View File

@ -0,0 +1,675 @@
/** \file
* \ingroup gpu
*/
#include "BLI_math_base.h"
#include "BLI_math_bits.h"
#include "GPU_framebuffer.h"
#include "mtl_context.hh"
#include "mtl_state.hh"
namespace blender::gpu {
/* -------------------------------------------------------------------- */
/** \name MTLStateManager
* \{ */
void MTLStateManager::mtl_state_init(void)
{
BLI_assert(this->context_);
this->context_->pipeline_state_init();
}
MTLStateManager::MTLStateManager(MTLContext *ctx) : StateManager()
{
/* Initialise State. */
this->context_ = ctx;
mtl_state_init();
/* Force update using default state. */
current_ = ~state;
current_mutable_ = ~mutable_state;
set_state(state);
set_mutable_state(mutable_state);
}
void MTLStateManager::apply_state(void)
{
this->set_state(this->state);
this->set_mutable_state(this->mutable_state);
/* TODO(Metal): Enable after integration of MTLFrameBuffer. */
/* static_cast<MTLFrameBuffer *>(this->context_->active_fb)->apply_state(); */
};
void MTLStateManager::force_state(void)
{
/* Little exception for clip distances since they need to keep the old count correct. */
uint32_t clip_distances = current_.clip_distances;
current_ = ~this->state;
current_.clip_distances = clip_distances;
current_mutable_ = ~this->mutable_state;
this->set_state(this->state);
this->set_mutable_state(this->mutable_state);
};
void MTLStateManager::set_state(const GPUState &state)
{
GPUState changed = state ^ current_;
if (changed.blend != 0) {
set_blend((eGPUBlend)state.blend);
}
if (changed.write_mask != 0) {
set_write_mask((eGPUWriteMask)state.write_mask);
}
if (changed.depth_test != 0) {
set_depth_test((eGPUDepthTest)state.depth_test);
}
if (changed.stencil_test != 0 || changed.stencil_op != 0) {
set_stencil_test((eGPUStencilTest)state.stencil_test, (eGPUStencilOp)state.stencil_op);
set_stencil_mask((eGPUStencilTest)state.stencil_test, mutable_state);
}
if (changed.clip_distances != 0) {
set_clip_distances(state.clip_distances, current_.clip_distances);
}
if (changed.culling_test != 0) {
set_backface_culling((eGPUFaceCullTest)state.culling_test);
}
if (changed.logic_op_xor != 0) {
set_logic_op(state.logic_op_xor);
}
if (changed.invert_facing != 0) {
set_facing(state.invert_facing);
}
if (changed.provoking_vert != 0) {
set_provoking_vert((eGPUProvokingVertex)state.provoking_vert);
}
if (changed.shadow_bias != 0) {
set_shadow_bias(state.shadow_bias);
}
/* TODO remove (Following GLState). */
if (changed.polygon_smooth) {
/* Note: Unsupported in Metal. */
}
if (changed.line_smooth) {
/* Note: Unsupported in Metal. */
}
current_ = state;
}
void MTLStateManager::mtl_depth_range(float near, float far)
{
BLI_assert(this->context_);
BLI_assert(near >= 0.0 && near < 1.0);
BLI_assert(far > 0.0 && far <= 1.0);
MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state;
ds_state.depth_range_near = near;
ds_state.depth_range_far = far;
pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_VIEWPORT_FLAG;
}
void MTLStateManager::set_mutable_state(const GPUStateMutable &state)
{
GPUStateMutable changed = state ^ current_mutable_;
MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
if (float_as_uint(changed.point_size) != 0) {
pipeline_state.point_size = state.point_size;
pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG;
}
if (changed.line_width != 0) {
pipeline_state.line_width = state.line_width;
pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG;
}
if (changed.depth_range[0] != 0 || changed.depth_range[1] != 0) {
/* TODO remove, should modify the projection matrix instead. */
mtl_depth_range(state.depth_range[0], state.depth_range[1]);
}
if (changed.stencil_compare_mask != 0 || changed.stencil_reference != 0 ||
changed.stencil_write_mask != 0) {
set_stencil_mask((eGPUStencilTest)current_.stencil_test, state);
}
current_mutable_ = state;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name State setting functions
* \{ */
void MTLStateManager::set_write_mask(const eGPUWriteMask value)
{
BLI_assert(this->context_);
MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
pipeline_state.depth_stencil_state.depth_write_enable = ((value & GPU_WRITE_DEPTH) != 0);
pipeline_state.color_write_mask =
(((value & GPU_WRITE_RED) != 0) ? MTLColorWriteMaskRed : MTLColorWriteMaskNone) |
(((value & GPU_WRITE_GREEN) != 0) ? MTLColorWriteMaskGreen : MTLColorWriteMaskNone) |
(((value & GPU_WRITE_BLUE) != 0) ? MTLColorWriteMaskBlue : MTLColorWriteMaskNone) |
(((value & GPU_WRITE_ALPHA) != 0) ? MTLColorWriteMaskAlpha : MTLColorWriteMaskNone);
pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG;
}
static MTLCompareFunction gpu_depth_function_to_metal(eGPUDepthTest depth_func)
{
switch (depth_func) {
case GPU_DEPTH_NONE:
return MTLCompareFunctionNever;
case GPU_DEPTH_LESS:
return MTLCompareFunctionLess;
case GPU_DEPTH_EQUAL:
return MTLCompareFunctionEqual;
case GPU_DEPTH_LESS_EQUAL:
return MTLCompareFunctionLessEqual;
case GPU_DEPTH_GREATER:
return MTLCompareFunctionGreater;
case GPU_DEPTH_GREATER_EQUAL:
return MTLCompareFunctionGreaterEqual;
case GPU_DEPTH_ALWAYS:
return MTLCompareFunctionAlways;
default:
BLI_assert(false && "Invalid eGPUDepthTest");
break;
}
return MTLCompareFunctionAlways;
}
static MTLCompareFunction gpu_stencil_func_to_metal(eGPUStencilTest stencil_func)
{
switch (stencil_func) {
case GPU_STENCIL_NONE:
return MTLCompareFunctionAlways;
case GPU_STENCIL_EQUAL:
return MTLCompareFunctionEqual;
case GPU_STENCIL_NEQUAL:
return MTLCompareFunctionNotEqual;
case GPU_STENCIL_ALWAYS:
return MTLCompareFunctionAlways;
default:
BLI_assert(false && "Unrecognised eGPUStencilTest function");
break;
}
return MTLCompareFunctionAlways;
}
void MTLStateManager::set_depth_test(const eGPUDepthTest value)
{
BLI_assert(this->context_);
MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state;
ds_state.depth_test_enabled = (value != GPU_DEPTH_NONE);
ds_state.depth_function = gpu_depth_function_to_metal(value);
pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG;
}
void MTLStateManager::mtl_stencil_mask(unsigned int mask)
{
BLI_assert(this->context_);
MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
pipeline_state.depth_stencil_state.stencil_write_mask = mask;
pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG;
}
void MTLStateManager::mtl_stencil_set_func(eGPUStencilTest stencil_func,
int ref,
unsigned int mask)
{
BLI_assert(this->context_);
MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state;
ds_state.stencil_func = gpu_stencil_func_to_metal(stencil_func);
ds_state.stencil_ref = ref;
ds_state.stencil_read_mask = mask;
pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG;
}
static void mtl_stencil_set_op_separate(MTLContext *context,
eGPUFaceCullTest face,
MTLStencilOperation stencil_fail,
MTLStencilOperation depth_test_fail,
MTLStencilOperation depthstencil_pass)
{
BLI_assert(context);
MTLContextGlobalShaderPipelineState &pipeline_state = context->pipeline_state;
MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state;
if (face == GPU_CULL_FRONT) {
ds_state.stencil_op_front_stencil_fail = stencil_fail;
ds_state.stencil_op_front_depth_fail = depth_test_fail;
ds_state.stencil_op_front_depthstencil_pass = depthstencil_pass;
}
else if (face == GPU_CULL_BACK) {
ds_state.stencil_op_back_stencil_fail = stencil_fail;
ds_state.stencil_op_back_depth_fail = depth_test_fail;
ds_state.stencil_op_back_depthstencil_pass = depthstencil_pass;
}
pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG;
}
static void mtl_stencil_set_op(MTLContext *context,
MTLStencilOperation stencil_fail,
MTLStencilOperation depth_test_fail,
MTLStencilOperation depthstencil_pass)
{
mtl_stencil_set_op_separate(
context, GPU_CULL_FRONT, stencil_fail, depth_test_fail, depthstencil_pass);
mtl_stencil_set_op_separate(
context, GPU_CULL_BACK, stencil_fail, depth_test_fail, depthstencil_pass);
}
void MTLStateManager::set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation)
{
switch (operation) {
case GPU_STENCIL_OP_REPLACE:
mtl_stencil_set_op(this->context_,
MTLStencilOperationKeep,
MTLStencilOperationKeep,
MTLStencilOperationReplace);
break;
case GPU_STENCIL_OP_COUNT_DEPTH_PASS:
/* Winding inversed due to flipped Y coordinate system in Metal. */
mtl_stencil_set_op_separate(this->context_,
GPU_CULL_FRONT,
MTLStencilOperationKeep,
MTLStencilOperationKeep,
MTLStencilOperationIncrementWrap);
mtl_stencil_set_op_separate(this->context_,
GPU_CULL_BACK,
MTLStencilOperationKeep,
MTLStencilOperationKeep,
MTLStencilOperationDecrementWrap);
break;
case GPU_STENCIL_OP_COUNT_DEPTH_FAIL:
/* Winding inversed due to flipped Y coordinate system in Metal. */
mtl_stencil_set_op_separate(this->context_,
GPU_CULL_FRONT,
MTLStencilOperationKeep,
MTLStencilOperationDecrementWrap,
MTLStencilOperationKeep);
mtl_stencil_set_op_separate(this->context_,
GPU_CULL_BACK,
MTLStencilOperationKeep,
MTLStencilOperationIncrementWrap,
MTLStencilOperationKeep);
break;
case GPU_STENCIL_OP_NONE:
default:
mtl_stencil_set_op(this->context_,
MTLStencilOperationKeep,
MTLStencilOperationKeep,
MTLStencilOperationKeep);
}
BLI_assert(this->context_);
MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
pipeline_state.depth_stencil_state.stencil_test_enabled = (test != GPU_STENCIL_NONE);
pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG;
}
void MTLStateManager::set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state)
{
if (test == GPU_STENCIL_NONE) {
mtl_stencil_mask(0x00);
mtl_stencil_set_func(GPU_STENCIL_ALWAYS, 0x00, 0x00);
}
else {
mtl_stencil_mask(state.stencil_write_mask);
mtl_stencil_set_func(test, state.stencil_reference, state.stencil_compare_mask);
}
}
void MTLStateManager::set_clip_distances(const int new_dist_len, const int old_dist_len)
{
/* TODO(Metal): Support Clip distances in METAL. Clip distance
* assignment via shader is supported, but global clip-states require
* support. */
}
void MTLStateManager::set_logic_op(const bool enable)
{
/* Note(Metal): Logic Operations not directly supported. */
}
void MTLStateManager::set_facing(const bool invert)
{
/* Check Current Context. */
BLI_assert(this->context_);
MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
/* Apply State -- opposite of GL, as METAL default is GPU_CLOCKWISE, GL default is
* COUNTERCLOCKWISE. This needs to be the inverse of the default. */
pipeline_state.front_face = (invert) ? GPU_COUNTERCLOCKWISE : GPU_CLOCKWISE;
/* Mark Dirty - Ensure context updates state between draws. */
pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_FRONT_FACING_FLAG;
pipeline_state.dirty = true;
}
void MTLStateManager::set_backface_culling(const eGPUFaceCullTest test)
{
/* Check Current Context. */
BLI_assert(this->context_);
MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
/* Apply State. */
pipeline_state.culling_enabled = (test != GPU_CULL_NONE);
pipeline_state.cull_mode = test;
/* Mark Dirty - Ensure context updates state between draws. */
pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_CULLMODE_FLAG;
pipeline_state.dirty = true;
}
void MTLStateManager::set_provoking_vert(const eGPUProvokingVertex vert)
{
/* Note(Metal): Provoking vertex is not a feature in the Metal API.
* Shaders are handled on a case-by-case basis using a modified vertex shader.
* For example, wireframe rendering and edit-mesh shaders utilise an SSBO-based
* vertex fetching mechanism which considers the inverse convention for flat
* shading, to ensure consistent results with OpenGL. */
}
void MTLStateManager::set_shadow_bias(const bool enable)
{
/* Check Current Context. */
BLI_assert(this->context_);
MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state;
/* Apply State. */
if (enable) {
ds_state.depth_bias_enabled_for_lines = true;
ds_state.depth_bias_enabled_for_tris = true;
ds_state.depth_bias = 2.0f;
ds_state.depth_slope_scale = 1.0f;
}
else {
ds_state.depth_bias_enabled_for_lines = false;
ds_state.depth_bias_enabled_for_tris = false;
ds_state.depth_bias = 0.0f;
ds_state.depth_slope_scale = 0.0f;
}
/* Mark Dirty - Ensure context updates depth-stencil state between draws. */
pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG;
pipeline_state.dirty = true;
}
void MTLStateManager::set_blend(const eGPUBlend value)
{
/**
* Factors to the equation.
* SRC is fragment shader output.
* DST is framebuffer color.
* final.rgb = SRC.rgb * src_rgb + DST.rgb * dst_rgb;
* final.a = SRC.a * src_alpha + DST.a * dst_alpha;
**/
MTLBlendFactor src_rgb;
MTLBlendFactor dst_rgb;
MTLBlendFactor src_alpha;
MTLBlendFactor dst_alpha;
switch (value) {
default:
case GPU_BLEND_ALPHA: {
src_rgb = MTLBlendFactorSourceAlpha;
dst_rgb = MTLBlendFactorOneMinusSourceAlpha;
src_alpha = MTLBlendFactorOne;
dst_alpha = MTLBlendFactorOneMinusSourceAlpha;
break;
}
case GPU_BLEND_ALPHA_PREMULT: {
src_rgb = MTLBlendFactorOne;
dst_rgb = MTLBlendFactorOneMinusSourceAlpha;
src_alpha = MTLBlendFactorOne;
dst_alpha = MTLBlendFactorOneMinusSourceAlpha;
break;
}
case GPU_BLEND_ADDITIVE: {
/* Do not let alpha accumulate but premult the source RGB by it. */
src_rgb = MTLBlendFactorSourceAlpha;
dst_rgb = MTLBlendFactorOne;
src_alpha = MTLBlendFactorZero;
dst_alpha = MTLBlendFactorOne;
break;
}
case GPU_BLEND_SUBTRACT:
case GPU_BLEND_ADDITIVE_PREMULT: {
/* Let alpha accumulate. */
src_rgb = MTLBlendFactorOne;
dst_rgb = MTLBlendFactorOne;
src_alpha = MTLBlendFactorOne;
dst_alpha = MTLBlendFactorOne;
break;
}
case GPU_BLEND_MULTIPLY: {
src_rgb = MTLBlendFactorDestinationColor;
dst_rgb = MTLBlendFactorZero;
src_alpha = MTLBlendFactorDestinationAlpha;
dst_alpha = MTLBlendFactorZero;
break;
}
case GPU_BLEND_INVERT: {
src_rgb = MTLBlendFactorOneMinusDestinationColor;
dst_rgb = MTLBlendFactorZero;
src_alpha = MTLBlendFactorZero;
dst_alpha = MTLBlendFactorOne;
break;
}
case GPU_BLEND_OIT: {
src_rgb = MTLBlendFactorOne;
dst_rgb = MTLBlendFactorOne;
src_alpha = MTLBlendFactorZero;
dst_alpha = MTLBlendFactorOneMinusSourceAlpha;
break;
}
case GPU_BLEND_BACKGROUND: {
src_rgb = MTLBlendFactorOneMinusDestinationAlpha;
dst_rgb = MTLBlendFactorSourceAlpha;
src_alpha = MTLBlendFactorZero;
dst_alpha = MTLBlendFactorSourceAlpha;
break;
}
case GPU_BLEND_ALPHA_UNDER_PREMUL: {
src_rgb = MTLBlendFactorOneMinusDestinationAlpha;
dst_rgb = MTLBlendFactorOne;
src_alpha = MTLBlendFactorOneMinusDestinationAlpha;
dst_alpha = MTLBlendFactorOne;
break;
}
case GPU_BLEND_CUSTOM: {
src_rgb = MTLBlendFactorOne;
dst_rgb = MTLBlendFactorSource1Color;
src_alpha = MTLBlendFactorOne;
dst_alpha = MTLBlendFactorSource1Alpha;
break;
}
}
/* Check Current Context. */
BLI_assert(this->context_);
MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state;
if (value == GPU_BLEND_SUBTRACT) {
pipeline_state.rgb_blend_op = MTLBlendOperationReverseSubtract;
pipeline_state.alpha_blend_op = MTLBlendOperationReverseSubtract;
}
else {
pipeline_state.rgb_blend_op = MTLBlendOperationAdd;
pipeline_state.alpha_blend_op = MTLBlendOperationAdd;
}
/* Apply State. */
pipeline_state.blending_enabled = (value != GPU_BLEND_NONE);
pipeline_state.src_rgb_blend_factor = src_rgb;
pipeline_state.dest_rgb_blend_factor = dst_rgb;
pipeline_state.src_alpha_blend_factor = src_alpha;
pipeline_state.dest_alpha_blend_factor = dst_alpha;
/* Mark Dirty - Ensure context updates PSOs between draws. */
pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG;
pipeline_state.dirty = true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Memory barrier
* \{ */
/* Note(Metal): Granular option for specifying before/after stages for a barrier
* Would be a useful feature. */
/*void MTLStateManager::issue_barrier(eGPUBarrier barrier_bits,
eGPUStageBarrierBits before_stages,
eGPUStageBarrierBits after_stages) */
void MTLStateManager::issue_barrier(eGPUBarrier barrier_bits)
{
/* Note(Metal): The Metal API implictly tracks dependencies between resources.
* Memory barriers and execution barriers (Fences/Events) can be used to coordinate
* this explicitly, however, in most cases, the driver will be able to
* resolve these dependencies automatically.
* For untracked resources, such as MTLHeap's, explicit barriers are necessary. */
eGPUStageBarrierBits before_stages = GPU_BARRIER_STAGE_ANY;
eGPUStageBarrierBits after_stages = GPU_BARRIER_STAGE_ANY;
MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get());
BLI_assert(ctx);
if (ctx->is_render_pass_active()) {
/* Apple Silicon does not support memory barriers.
* We do not currently need these due to implicit API guarantees.
* Note(Metal): MTLFence/MTLEvent may be required to synchronize work if
* untracked resources are ever used. */
if ([ctx->device hasUnifiedMemory]) {
return;
}
/* Issue barrier. */
/* TODO(Metal): To be completed pending implementation of RenderCommandEncoder management. */
id<MTLRenderCommandEncoder> rec = nil; // ctx->get_active_render_command_encoder();
BLI_assert(rec);
/* Only supporting Metal on 10.15 onwards anyway - Check required for warnings. */
if (@available(macOS 10.14, *)) {
MTLBarrierScope scope = 0;
if (barrier_bits & GPU_BARRIER_SHADER_IMAGE_ACCESS ||
barrier_bits & GPU_BARRIER_TEXTURE_FETCH) {
scope = scope | MTLBarrierScopeTextures | MTLBarrierScopeRenderTargets;
}
if (barrier_bits & GPU_BARRIER_SHADER_STORAGE ||
barrier_bits & GPU_BARRIER_VERTEX_ATTRIB_ARRAY ||
barrier_bits & GPU_BARRIER_ELEMENT_ARRAY) {
scope = scope | MTLBarrierScopeBuffers;
}
MTLRenderStages before_stage_flags = 0;
MTLRenderStages after_stage_flags = 0;
if (before_stages & GPU_BARRIER_STAGE_VERTEX &&
!(before_stages & GPU_BARRIER_STAGE_FRAGMENT)) {
before_stage_flags = before_stage_flags | MTLRenderStageVertex;
}
if (before_stages & GPU_BARRIER_STAGE_FRAGMENT) {
before_stage_flags = before_stage_flags | MTLRenderStageFragment;
}
if (after_stages & GPU_BARRIER_STAGE_VERTEX) {
after_stage_flags = after_stage_flags | MTLRenderStageVertex;
}
if (after_stages & GPU_BARRIER_STAGE_FRAGMENT) {
after_stage_flags = MTLRenderStageFragment;
}
if (scope != 0) {
[rec memoryBarrierWithScope:scope
afterStages:after_stage_flags
beforeStages:before_stage_flags];
}
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Texture State Management
* \{ */
void MTLStateManager::texture_unpack_row_length_set(uint len)
{
/* Set source image row data stride when uploading image data to the GPU. */
MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
ctx->pipeline_state.unpack_row_length = len;
}
void MTLStateManager::texture_bind(Texture *tex_, eGPUSamplerState sampler_type, int unit)
{
BLI_assert(tex_);
gpu::MTLTexture *mtl_tex = static_cast<gpu::MTLTexture *>(tex_);
BLI_assert(mtl_tex);
MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
if (unit >= 0) {
ctx->texture_bind(mtl_tex, unit);
/* Fetching textures default sampler configuration and applying
* eGPUSampler State on top. This path exists to support
* Any of the sampler state which is associated with the
* texture itself such as min/max mip levels. */
MTLSamplerState sampler = mtl_tex->get_sampler_state();
sampler.state = sampler_type;
ctx->sampler_bind(sampler, unit);
}
}
void MTLStateManager::texture_unbind(Texture *tex_)
{
BLI_assert(tex_);
gpu::MTLTexture *mtl_tex = static_cast<gpu::MTLTexture *>(tex_);
BLI_assert(mtl_tex);
MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
ctx->texture_unbind(mtl_tex);
}
void MTLStateManager::texture_unbind_all(void)
{
MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
BLI_assert(ctx);
ctx->texture_unbind_all();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Image Binding (from image load store)
* \{ */
void MTLStateManager::image_bind(Texture *tex_, int unit)
{
this->texture_bind(tex_, GPU_SAMPLER_DEFAULT, unit);
}
void MTLStateManager::image_unbind(Texture *tex_)
{
this->texture_unbind(tex_);
}
void MTLStateManager::image_unbind_all(void)
{
this->texture_unbind_all();
}
/** \} */
} // blender::gpu

View File

@ -47,16 +47,13 @@ struct TextureUpdateRoutineSpecialisation {
(component_count_input == other.component_count_input) &&
(component_count_output == other.component_count_output));
}
};
template<> struct blender::DefaultHash<TextureUpdateRoutineSpecialisation> {
inline uint64_t operator()(const TextureUpdateRoutineSpecialisation &key) const
inline uint64_t hash() const
{
DefaultHash<std::string> string_hasher;
blender::DefaultHash<std::string> string_hasher;
return (uint64_t)string_hasher(
key.input_data_type + key.output_data_type +
std::to_string((key.component_count_input << 8) + key.component_count_output));
this->input_data_type + this->output_data_type +
std::to_string((this->component_count_input << 8) + this->component_count_output));
}
};
@ -78,12 +75,10 @@ struct DepthTextureUpdateRoutineSpecialisation {
{
return ((data_mode == other.data_mode));
}
};
template<> struct blender::DefaultHash<DepthTextureUpdateRoutineSpecialisation> {
inline uint64_t operator()(const DepthTextureUpdateRoutineSpecialisation &key) const
inline uint64_t hash() const
{
return (uint64_t)(key.data_mode);
return (uint64_t)(this->data_mode);
}
};
@ -109,17 +104,14 @@ struct TextureReadRoutineSpecialisation {
(component_count_output == other.component_count_output) &&
(depth_format_mode == other.depth_format_mode));
}
};
template<> struct blender::DefaultHash<TextureReadRoutineSpecialisation> {
inline uint64_t operator()(const TextureReadRoutineSpecialisation &key) const
inline uint64_t hash() const
{
DefaultHash<std::string> string_hasher;
return (uint64_t)string_hasher(key.input_data_type + key.output_data_type +
std::to_string((key.component_count_input << 8) +
key.component_count_output +
(key.depth_format_mode << 28)));
blender::DefaultHash<std::string> string_hasher;
return (uint64_t)string_hasher(this->input_data_type + this->output_data_type +
std::to_string((this->component_count_input << 8) +
this->component_count_output +
(this->depth_format_mode << 28)));
}
};
@ -158,21 +150,6 @@ typedef struct MTLSamplerState {
const MTLSamplerState DEFAULT_SAMPLER_STATE = {GPU_SAMPLER_DEFAULT /*, 0, 9999*/};
} // namespace blender::gpu
template<> struct blender::DefaultHash<blender::gpu::MTLSamplerState> {
inline uint64_t operator()(const blender::gpu::MTLSamplerState &key) const
{
const DefaultHash<unsigned int> uint_hasher;
uint64_t main_hash = (uint64_t)uint_hasher((unsigned int)(key.state));
/* Hash other parameters as needed. */
return main_hash;
}
};
namespace blender::gpu {
class MTLTexture : public Texture {
friend class MTLContext;
friend class MTLStateManager;