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:
parent
992ae3f282
commit
073139e329
Notes:
blender-bot
2023-02-14 08:39:23 +01:00
Referenced by issue #96261, Metal Viewport
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue