From 49d1e6a93272de9a29215d482f8f5b90abf6f2b9 Mon Sep 17 00:00:00 2001
From: benstevens48
Date: Fri, 3 Jan 2025 16:03:13 +0000
Subject: [PATCH 01/12] initial pixel shader effect cache
---
winrt/lib/effects/shader/ClipTransform.cpp | 2 +-
.../effects/shader/PixelShaderEffect.abi.idl | 25 ++++-
.../lib/effects/shader/PixelShaderEffect.cpp | 104 +++++++++++++++++-
winrt/lib/effects/shader/PixelShaderEffect.h | 25 ++++-
.../effects/shader/PixelShaderEffectImpl.cpp | 4 +-
.../effects/shader/PixelShaderTransform.cpp | 8 +-
winrt/lib/effects/shader/ShaderDescription.h | 23 ++++
.../lib/effects/shader/SharedShaderState.cpp | 92 ++++++++--------
winrt/lib/effects/shader/SharedShaderState.h | 33 ++----
winrt/lib/utils/Strings.inl | 1 +
.../graphics/PixelShaderEffectUnitTests.cpp | 86 +++++++--------
11 files changed, 279 insertions(+), 124 deletions(-)
diff --git a/winrt/lib/effects/shader/ClipTransform.cpp b/winrt/lib/effects/shader/ClipTransform.cpp
index 7187c6513..754159380 100644
--- a/winrt/lib/effects/shader/ClipTransform.cpp
+++ b/winrt/lib/effects/shader/ClipTransform.cpp
@@ -20,7 +20,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
// PixelShaderTransform inputs, before any intermediate border transforms were applied to them.
// This lets us compute a non infinite output rect even in the presence of hard borders.
- return 1 + m_sharedState->Shader().InputCount;
+ return 1 + m_sharedState->Shader()->InputCount;
}
diff --git a/winrt/lib/effects/shader/PixelShaderEffect.abi.idl b/winrt/lib/effects/shader/PixelShaderEffect.abi.idl
index 83ed561bd..bbee3039e 100644
--- a/winrt/lib/effects/shader/PixelShaderEffect.abi.idl
+++ b/winrt/lib/effects/shader/PixelShaderEffect.abi.idl
@@ -14,6 +14,29 @@ namespace Microsoft.Graphics.Canvas.Effects
Offset
} SamplerCoordinateMapping;
+ [version(VERSION), uuid(39D6BA64-E00D-4E14-958C-F1329CE1011F), exclusiveto(PixelShaderEffect)]
+ interface IPixelShaderEffectStatics : IInspectable
+ {
+ HRESULT RegisterEffect(
+ [in] UINT32 shaderCodeCount,
+ [in, size_is(shaderCodeCount)] BYTE* shaderCode,
+ [in] GUID effectId);
+
+ HRESULT CreateEffect(
+ [in] GUID effectId,
+ [out, retval] PixelShaderEffect** effect);
+
+ HRESULT RegisterAndCreateEffect(
+ [in] UINT32 shaderCodeCount,
+ [in, size_is(shaderCodeCount)] BYTE* shaderCode,
+ [in] GUID effectId,
+ [out, retval] PixelShaderEffect** effect);
+
+ HRESULT IsEffectRegistered([in] GUID effectId, [out, retval] boolean* result);
+
+ HRESULT UnregisterEffect([in] GUID effectId);
+ };
+
[version(VERSION), uuid(FC8C3C31-FA96-45E2-8B72-1741C65CEE8E), exclusiveto(PixelShaderEffect)]
interface IPixelShaderEffect : IInspectable
requires ICanvasEffect
@@ -107,7 +130,7 @@ namespace Microsoft.Graphics.Canvas.Effects
[out, retval] PixelShaderEffect** effect);
};
- [version(VERSION), activatable(IPixelShaderEffectFactory, VERSION)]
+ [version(VERSION), activatable(IPixelShaderEffectFactory, VERSION), static(IPixelShaderEffectStatics, VERSION)]
runtimeclass PixelShaderEffect
{
[default] interface IPixelShaderEffect;
diff --git a/winrt/lib/effects/shader/PixelShaderEffect.cpp b/winrt/lib/effects/shader/PixelShaderEffect.cpp
index 264287717..6e94b962e 100644
--- a/winrt/lib/effects/shader/PixelShaderEffect.cpp
+++ b/winrt/lib/effects/shader/PixelShaderEffect.cpp
@@ -31,6 +31,100 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
});
}
+ IFACEMETHODIMP PixelShaderEffectFactory::RegisterEffect(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId)
+ {
+ return ExceptionBoundary([&]
+ {
+ if (effectId == GUID_NULL) {
+ ThrowHR(E_INVALIDARG);
+ }
+
+ {
+ std::lock_guard lock(m_mutex);
+ auto const& cached = shaderCache.find(effectId);
+ if (cached != shaderCache.end()) {
+ return;
+ }
+ }
+
+ CheckInPointer(shaderCode);
+ auto description = SharedShaderState::CreateShaderDescription(shaderCode, shaderCodeCount, effectId);
+
+ {
+ std::lock_guard lock(m_mutex);
+ auto const& cached = shaderCache.find(effectId);
+ if (cached != shaderCache.end()) {
+ return;
+ }
+ shaderCache.insert_or_assign(effectId, description);
+ }
+ });
+ }
+
+ IFACEMETHODIMP PixelShaderEffectFactory::CreateEffect(GUID effectId, IPixelShaderEffect** effect)
+ {
+ return ExceptionBoundary([&]
+ {
+ CheckAndClearOutPointer(effect);
+
+ std::shared_ptr description{ nullptr };
+ {
+ std::lock_guard lock(m_mutex);
+ auto const& cached = shaderCache.find(effectId);
+ if (cached != shaderCache.end()) {
+ description = cached->second;
+ }
+ }
+
+ if (description == nullptr) {
+ ThrowHR(E_INVALIDARG, Strings::CustomEffectNotRegistered);
+ }
+
+ // Create a shared state object using the specified shader code.
+ auto sharedState = Make(description, description->DefaultConstants, description->DefaultCoordinateMapping, SourceInterpolationState());
+ CheckMakeResult(sharedState);
+
+ // Create the WinRT effect instance.
+ auto newEffect = Make(nullptr, nullptr, sharedState.Get());
+ CheckMakeResult(newEffect);
+
+ ThrowIfFailed(newEffect.CopyTo(effect));
+ });
+ }
+
+ IFACEMETHODIMP PixelShaderEffectFactory::RegisterAndCreateEffect(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId, IPixelShaderEffect** effect)
+ {
+ return ExceptionBoundary([&]
+ {
+ ThrowIfFailed(RegisterEffect(shaderCodeCount, shaderCode, effectId));
+ ThrowIfFailed(CreateEffect(effectId, effect));
+ });
+ }
+
+ IFACEMETHODIMP PixelShaderEffectFactory::IsEffectRegistered(GUID effectId, boolean* result)
+ {
+ return ExceptionBoundary([&]
+ {
+ CheckInPointer(result);
+
+ std::lock_guard lock(m_mutex);
+ auto const& cached = shaderCache.find(effectId);
+ *result = cached != shaderCache.end();
+ });
+ }
+
+ IFACEMETHODIMP PixelShaderEffectFactory::UnregisterEffect(GUID effectId)
+ {
+ return ExceptionBoundary([&]
+ {
+ std::lock_guard lock(m_mutex);
+ shaderCache.erase(effectId);
+ });
+ }
+
+ std::recursive_mutex PixelShaderEffectFactory::m_mutex{};
+ std::map, GuidComparer> PixelShaderEffectFactory::shaderCache{};
+
// Describe how to implement WinRT IMap<> methods in terms of our shader constant buffer.
template
@@ -81,7 +175,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
PixelShaderEffect::PixelShaderEffect(ICanvasDevice* device, ID2D1Effect* effect, ISharedShaderState* sharedState)
- : CanvasEffect(CLSID_PixelShaderEffect, 0, sharedState->Shader().InputCount, true, device, effect, static_cast(this))
+ : CanvasEffect(CLSID_PixelShaderEffect, 0, sharedState->Shader()->InputCount, true, device, effect, static_cast(this))
, m_sharedState(sharedState)
{
m_propertyMap = Make(true, this);
@@ -210,7 +304,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
ThrowIfFailed(As(device)->GetInterface(IID_PPV_ARGS(&d3dDevice)));
auto deviceFeatureLevel = d3dDevice->GetFeatureLevel();
- auto shaderFeatureLevel = m_sharedState->Shader().MinFeatureLevel;
+ auto shaderFeatureLevel = m_sharedState->Shader()->MinFeatureLevel;
return deviceFeatureLevel >= shaderFeatureLevel;
}
@@ -263,7 +357,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
IFACEMETHODIMP PixelShaderEffect::GetSource(unsigned int index, IGraphicsEffectSource** source)
{
- if (index >= m_sharedState->Shader().InputCount && source)
+ if (index >= m_sharedState->Shader()->InputCount && source)
{
// Getting an out of range source returns null, rather than throwing like most effects.
*source = nullptr;
@@ -279,14 +373,14 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
void PixelShaderEffect::SetSource(unsigned int index, IGraphicsEffectSource* source)
{
- if (index >= m_sharedState->Shader().InputCount)
+ if (index >= m_sharedState->Shader()->InputCount)
{
// Setting unused sources to null is valid.
// If the value is not null, we format a nice exception message.
if (source)
{
WinStringBuilder message;
- message.Format(Strings::CustomEffectSourceOutOfRange, index + 1, m_sharedState->Shader().InputCount);
+ message.Format(Strings::CustomEffectSourceOutOfRange, index + 1, m_sharedState->Shader()->InputCount);
ThrowHR(E_INVALIDARG, message.Get());
}
}
diff --git a/winrt/lib/effects/shader/PixelShaderEffect.h b/winrt/lib/effects/shader/PixelShaderEffect.h
index 6760b8a9a..695ac96b3 100644
--- a/winrt/lib/effects/shader/PixelShaderEffect.h
+++ b/winrt/lib/effects/shader/PixelShaderEffect.h
@@ -4,6 +4,9 @@
#pragma once
+#include "Utils/GuidUtilities.h"
+#include "ShaderDescription.h"
+
namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { namespace Effects
{
class ISharedShaderState;
@@ -12,13 +15,33 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
// WinRT activation factory.
- class PixelShaderEffectFactory : public AgileActivationFactory
+ class PixelShaderEffectFactory : public AgileActivationFactory
, private LifespanTracker
{
InspectableClassStatic(RuntimeClass_Microsoft_Graphics_Canvas_Effects_PixelShaderEffect, BaseTrust);
public:
+ //
+ // IPixelShaderEffectFactory
+ //
IFACEMETHOD(Create)(uint32_t shaderCodeCount, BYTE* shaderCode, IPixelShaderEffect** effect) override;
+
+ //
+ // IPixelShaderEffectStatics
+ //
+ IFACEMETHOD(RegisterEffect)(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId) override;
+
+ IFACEMETHOD(CreateEffect)(GUID effectId, IPixelShaderEffect** effect) override;
+
+ IFACEMETHOD(RegisterAndCreateEffect)(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId, IPixelShaderEffect** effect) override;
+
+ IFACEMETHOD(IsEffectRegistered)(GUID effectId, boolean* result) override;
+
+ IFACEMETHOD(UnregisterEffect)(GUID effectId) override;
+
+ private:
+ static std::recursive_mutex m_mutex;
+ static std::map, GuidComparer> shaderCache;
};
diff --git a/winrt/lib/effects/shader/PixelShaderEffectImpl.cpp b/winrt/lib/effects/shader/PixelShaderEffectImpl.cpp
index 507556d57..8f46cdaec 100644
--- a/winrt/lib/effects/shader/PixelShaderEffectImpl.cpp
+++ b/winrt/lib/effects/shader/PixelShaderEffectImpl.cpp
@@ -168,7 +168,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
// Load our pixel shader into D2D.
auto& shader = m_sharedState->Shader();
- HRESULT hr = m_effectContext->LoadPixelShader(shader.Hash, shader.Code.data(), static_cast(shader.Code.size()));
+ HRESULT hr = m_effectContext->LoadPixelShader(shader->Hash, shader->Code.data(), static_cast(shader->Code.size()));
if (FAILED(hr))
ThrowHR(hr, Strings::CustomEffectBadShader);
@@ -197,7 +197,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
ThrowIfFailed(m_transformGraph->ConnectNode(shaderTransform.Get(), clipTransform.Get(), 0));
ThrowIfFailed(m_transformGraph->SetOutputNode(clipTransform.Get()));
- for (unsigned i = 0; i < m_sharedState->Shader().InputCount; i++)
+ for (unsigned i = 0; i < m_sharedState->Shader()->InputCount; i++)
{
switch (m_coordinateMapping->BorderMode[i])
{
diff --git a/winrt/lib/effects/shader/PixelShaderTransform.cpp b/winrt/lib/effects/shader/PixelShaderTransform.cpp
index 21a6aff10..db5a57f28 100644
--- a/winrt/lib/effects/shader/PixelShaderTransform.cpp
+++ b/winrt/lib/effects/shader/PixelShaderTransform.cpp
@@ -16,7 +16,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
IFACEMETHODIMP_(UINT32) PixelShaderTransform::GetInputCount() const
{
- return m_sharedState->Shader().InputCount;
+ return m_sharedState->Shader()->InputCount;
}
@@ -86,9 +86,9 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
// Tell D2D to use our pixel shader.
auto& shader = m_sharedState->Shader();
- ThrowIfFailed(drawInfo->SetPixelShader(shader.Hash));
+ ThrowIfFailed(drawInfo->SetPixelShader(shader->Hash));
- drawInfo->SetInstructionCountHint(shader.InstructionCount);
+ drawInfo->SetInstructionCountHint(shader->InstructionCount);
});
}
@@ -101,7 +101,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
void PixelShaderTransform::SetSourceInterpolation(SourceInterpolationState const* sourceInterpolation)
{
- auto inputCount = m_sharedState->Shader().InputCount;
+ auto inputCount = m_sharedState->Shader()->InputCount;
for (unsigned i = 0; i < inputCount; i++)
{
diff --git a/winrt/lib/effects/shader/ShaderDescription.h b/winrt/lib/effects/shader/ShaderDescription.h
index 87532d39d..a0bc01632 100644
--- a/winrt/lib/effects/shader/ShaderDescription.h
+++ b/winrt/lib/effects/shader/ShaderDescription.h
@@ -67,6 +67,26 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
bool operator() (HSTRING value1, ShaderVariable const& value2) const { return comparison(value1, value2.Name); }
};
+ const int MaxShaderInputs = 8;
+
+ // Describes how this shader maps between its input images and output locations.
+ struct CoordinateMappingState
+ {
+ CoordinateMappingState()
+ : MaxOffset(0)
+ {
+ for (int i = 0; i < MaxShaderInputs; i++)
+ {
+ Mapping[i] = SamplerCoordinateMapping::Unknown;
+ BorderMode[i] = EffectBorderMode::Soft;
+ }
+ }
+
+ SamplerCoordinateMapping Mapping[MaxShaderInputs];
+ EffectBorderMode BorderMode[MaxShaderInputs];
+ int MaxOffset;
+ };
+
// Copyable struct stores shader program code along with metadata describing how to use the shader.
struct ShaderDescription
@@ -90,6 +110,9 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
// Sorted by name.
std::vector Variables;
+
+ std::vector DefaultConstants;
+ CoordinateMappingState DefaultCoordinateMapping;
};
}}}}}
diff --git a/winrt/lib/effects/shader/SharedShaderState.cpp b/winrt/lib/effects/shader/SharedShaderState.cpp
index e7afe53bb..6b9947c1f 100644
--- a/winrt/lib/effects/shader/SharedShaderState.cpp
+++ b/winrt/lib/effects/shader/SharedShaderState.cpp
@@ -8,16 +8,6 @@
namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { namespace Effects
{
- CoordinateMappingState::CoordinateMappingState()
- : MaxOffset(0)
- {
- for (int i = 0; i < MaxShaderInputs; i++)
- {
- Mapping[i] = SamplerCoordinateMapping::Unknown;
- BorderMode[i] = EffectBorderMode::Soft;
- }
- }
-
SourceInterpolationState::SourceInterpolationState()
{
@@ -28,7 +18,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
}
- SharedShaderState::SharedShaderState(ShaderDescription const& shader, std::vector const& constants, CoordinateMappingState const& coordinateMapping, SourceInterpolationState const& sourceInterpolation)
+ SharedShaderState::SharedShaderState(std::shared_ptr const& shader, std::vector const& constants, CoordinateMappingState const& coordinateMapping, SourceInterpolationState const& sourceInterpolation)
: m_shader(shader)
, m_constants(constants)
, m_coordinateMapping(coordinateMapping)
@@ -38,16 +28,30 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
SharedShaderState::SharedShaderState(BYTE* shaderCode, uint32_t shaderCodeSize)
{
- // Store the shader program code.
- m_shader.Code.assign(shaderCode, shaderCode + shaderCodeSize);
-
- // Hash it to generate a unique ID.
+ // Hash the code to generate a unique ID.
static const IID salt{ 0x489257f6, 0x6544, 0x4277, 0x89, 0x82, 0xea, 0xd1, 0x69, 0x39, 0x1f, 0x3d };
- m_shader.Hash = GetVersion5Uuid(salt, shaderCode, shaderCodeSize);
+ auto hash = GetVersion5Uuid(salt, shaderCode, shaderCodeSize);
+
+ m_shader = CreateShaderDescription(shaderCode, shaderCodeSize, hash);
+ m_constants = m_shader->DefaultConstants;
+ m_coordinateMapping = m_shader->DefaultCoordinateMapping;
+ }
+
+
+ std::shared_ptr SharedShaderState::CreateShaderDescription(BYTE* shaderCode, uint32_t shaderCodeSize, IID const& effectId)
+ {
+ auto shader = std::make_shared();
+
+ // Store the shader program code.
+ shader->Code.assign(shaderCode, shaderCode + shaderCodeSize);
+
+ shader->Hash = effectId;
// Look up shader metadata.
- ReflectOverShader();
+ ReflectOverShader(shader);
+
+ return shader;
}
@@ -62,13 +66,13 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
unsigned SharedShaderState::GetPropertyCount()
{
- return static_cast(m_shader.Variables.size());
+ return static_cast(m_shader->Variables.size());
}
bool SharedShaderState::HasProperty(HSTRING name)
{
- return std::binary_search(m_shader.Variables.begin(), m_shader.Variables.end(), name, VariableNameComparison());
+ return std::binary_search(m_shader->Variables.begin(), m_shader->Variables.end(), name, VariableNameComparison());
}
@@ -210,9 +214,9 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
{
std::vector properties;
- properties.reserve(m_shader.Variables.size());
+ properties.reserve(m_shader->Variables.size());
- for (auto& variable : m_shader.Variables)
+ for (auto& variable : m_shader->Variables)
{
properties.emplace_back(variable.Name, GetProperty(variable));
}
@@ -225,9 +229,9 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
{
VariableNameComparison comparison;
- auto it = std::lower_bound(m_shader.Variables.begin(), m_shader.Variables.end(), name, comparison);
+ auto it = std::lower_bound(m_shader->Variables.begin(), m_shader->Variables.end(), name, comparison);
- if (it == m_shader.Variables.end() || comparison(name, *it))
+ if (it == m_shader->Variables.end() || comparison(name, *it))
{
WinStringBuilder message;
message.Format(Strings::CustomEffectUnknownProperty, WindowsGetStringRawBuffer(name, nullptr));
@@ -460,12 +464,12 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
}
- void SharedShaderState::ReflectOverShader()
+ void SharedShaderState::ReflectOverShader(std::shared_ptr const& outputDescription)
{
// Create the shader reflection interface.
ComPtr reflector;
- HRESULT hr = D3DReflect(m_shader.Code.data(), m_shader.Code.size(), IID_PPV_ARGS(&reflector));
+ HRESULT hr = D3DReflect(outputDescription->Code.data(), outputDescription->Code.size(), IID_PPV_ARGS(&reflector));
if (FAILED(hr))
ThrowHR(E_INVALIDARG, Strings::CustomEffectBadShader);
@@ -484,25 +488,25 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
}
// Examine the input bindings.
- ReflectOverBindings(reflector.Get(), desc);
+ ReflectOverBindings(outputDescription, reflector.Get(), desc);
// Store the mapping from named constants to buffer locations.
if (desc.ConstantBuffers)
{
- ReflectOverConstantBuffer(reflector->GetConstantBufferByIndex(0));
+ ReflectOverConstantBuffer(outputDescription, reflector->GetConstantBufferByIndex(0));
}
// Grab some other metadata.
- m_shader.InstructionCount = desc.InstructionCount;
+ outputDescription->InstructionCount = desc.InstructionCount;
- ThrowIfFailed(reflector->GetMinFeatureLevel(&m_shader.MinFeatureLevel));
+ ThrowIfFailed(reflector->GetMinFeatureLevel(&outputDescription->MinFeatureLevel));
// If this shader was compiled to support shader linking, we can also determine which inputs are simple vs. complex.
- ReflectOverShaderLinkingFunction();
+ ReflectOverShaderLinkingFunction(outputDescription);
}
- void SharedShaderState::ReflectOverBindings(ID3D11ShaderReflection* reflector, D3D11_SHADER_DESC const& desc)
+ void SharedShaderState::ReflectOverBindings(std::shared_ptr const& outputDescription, ID3D11ShaderReflection* reflector, D3D11_SHADER_DESC const& desc)
{
for (unsigned i = 0; i < desc.BoundResources; i++)
{
@@ -516,7 +520,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
ThrowHR(E_INVALIDARG, Strings::CustomEffectTooManyTextures);
// Record how many input textures this shader uses.
- m_shader.InputCount = std::max(m_shader.InputCount, inputDesc.BindPoint + 1);
+ outputDescription->InputCount = std::max(outputDescription->InputCount, inputDesc.BindPoint + 1);
break;
case D3D_SIT_CBUFFER:
@@ -529,24 +533,24 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
}
- void SharedShaderState::ReflectOverConstantBuffer(ID3D11ShaderReflectionConstantBuffer* constantBuffer)
+ void SharedShaderState::ReflectOverConstantBuffer(std::shared_ptr const& outputDescription, ID3D11ShaderReflectionConstantBuffer* constantBuffer)
{
D3D11_SHADER_BUFFER_DESC desc;
ThrowIfFailed(constantBuffer->GetDesc(&desc));
// Resize our constant buffer to match the shader.
- m_constants.resize(desc.Size);
+ outputDescription->DefaultConstants.resize(desc.Size);
// Look up variable metadata.
- m_shader.Variables.reserve(desc.Variables);
+ outputDescription->Variables.reserve(desc.Variables);
for (unsigned i = 0; i < desc.Variables; i++)
{
- ReflectOverVariable(constantBuffer->GetVariableByIndex(i));
+ ReflectOverVariable(outputDescription, constantBuffer->GetVariableByIndex(i));
}
// Sort the variables by name.
- std::sort(m_shader.Variables.begin(), m_shader.Variables.end(), VariableNameComparison());
+ std::sort(outputDescription->Variables.begin(), outputDescription->Variables.end(), VariableNameComparison());
}
@@ -629,7 +633,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
}
- void SharedShaderState::ReflectOverVariable(ID3D11ShaderReflectionVariable* variable)
+ void SharedShaderState::ReflectOverVariable(std::shared_ptr const& outputDescription, ID3D11ShaderReflectionVariable* variable)
{
D3D11_SHADER_VARIABLE_DESC desc;
ThrowIfFailed(variable->GetDesc(&desc));
@@ -652,7 +656,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
// This can only fail if the shader blob is corrupted.
auto endOffset = desc.StartOffset + desc.Size;
- if (endOffset > m_constants.size() || endOffset < desc.StartOffset)
+ if (endOffset > outputDescription->DefaultConstants.size() || endOffset < desc.StartOffset)
{
ThrowHR(E_UNEXPECTED);
}
@@ -660,15 +664,15 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
// Initialize our constant buffer with the default value of the variable.
if (desc.DefaultValue)
{
- CopyDefaultValue(m_constants.data() + desc.StartOffset, desc, type);
+ CopyDefaultValue(outputDescription->DefaultConstants.data() + desc.StartOffset, desc, type);
}
// Store metadata about this variable.
- m_shader.Variables.emplace_back(desc, type);
+ outputDescription->Variables.emplace_back(desc, type);
}
- void SharedShaderState::ReflectOverShaderLinkingFunction()
+ void SharedShaderState::ReflectOverShaderLinkingFunction(std::shared_ptr const& outputDescription)
{
// If this shader was compiled to support shader linking, we can get extra information
// (telling us which inputs are simple vs. complex) from the shader linking function.
@@ -676,7 +680,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
// It's valid to use shaders that don't support linking, so we return on failure rather than throwing.
ComPtr privateData;
- if (FAILED(D3DGetBlobPart(m_shader.Code.data(), m_shader.Code.size(), D3D_BLOB_PRIVATE_DATA, 0, &privateData)))
+ if (FAILED(D3DGetBlobPart(outputDescription->Code.data(), outputDescription->Code.size(), D3D_BLOB_PRIVATE_DATA, 0, &privateData)))
return;
ComPtr reflector;
@@ -711,7 +715,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
else if (strstr(parameterDesc.SemanticName, "INPUT"))
{
// INPUT semantic means a simple input, so select passthrough coordinate mapping mode.
- m_coordinateMapping.Mapping[inputCount++] = SamplerCoordinateMapping::OneToOne;
+ outputDescription->DefaultCoordinateMapping.Mapping[inputCount++] = SamplerCoordinateMapping::OneToOne;
}
}
}
diff --git a/winrt/lib/effects/shader/SharedShaderState.h b/winrt/lib/effects/shader/SharedShaderState.h
index d418870ce..d0a7bb449 100644
--- a/winrt/lib/effects/shader/SharedShaderState.h
+++ b/winrt/lib/effects/shader/SharedShaderState.h
@@ -12,20 +12,6 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
enum class CopyDirection { Read, Write };
- const int MaxShaderInputs = 8;
-
-
- // Describes how this shader maps between its input images and output locations.
- struct CoordinateMappingState
- {
- CoordinateMappingState();
-
- SamplerCoordinateMapping Mapping[MaxShaderInputs];
- EffectBorderMode BorderMode[MaxShaderInputs];
- int MaxOffset;
- };
-
-
// Configures the filtering mode used to sample shader source textures.
struct SourceInterpolationState
{
@@ -51,7 +37,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
public:
virtual ComPtr Clone() = 0;
- virtual ShaderDescription const& Shader() = 0;
+ virtual std::shared_ptr const& Shader() = 0;
virtual std::vector const& Constants() = 0;
virtual CoordinateMappingState& CoordinateMapping() = 0;
virtual SourceInterpolationState& SourceInterpolation() = 0;
@@ -68,18 +54,19 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
class SharedShaderState : public RuntimeClass, ISharedShaderState>
, private LifespanTracker
{
- ShaderDescription m_shader;
+ std::shared_ptr m_shader;
std::vector m_constants;
CoordinateMappingState m_coordinateMapping;
SourceInterpolationState m_sourceInterpolation;
public:
- SharedShaderState(ShaderDescription const& shader, std::vector const& constants, CoordinateMappingState const& coordinateMapping, SourceInterpolationState const& sourceInterpolation);
+ SharedShaderState(std::shared_ptr const& shader, std::vector const& constants, CoordinateMappingState const& coordinateMapping, SourceInterpolationState const& sourceInterpolation);
SharedShaderState(BYTE* shaderCode, uint32_t shaderCodeSize);
+ static std::shared_ptr SharedShaderState::CreateShaderDescription(BYTE* shaderCode, uint32_t shaderCodeSize, IID const& effectId);
virtual ComPtr Clone() override;
- virtual ShaderDescription const& Shader() override { return m_shader; }
+ virtual std::shared_ptr const& Shader() override { return m_shader; }
virtual std::vector const& Constants() override { return m_constants; }
virtual CoordinateMappingState& CoordinateMapping() override { return m_coordinateMapping; }
virtual SourceInterpolationState& SourceInterpolation() { return m_sourceInterpolation; }
@@ -108,11 +95,11 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
// Shader reflection (done at init time).
- void ReflectOverShader();
- void ReflectOverBindings(ID3D11ShaderReflection* reflector, D3D11_SHADER_DESC const& desc);
- void ReflectOverConstantBuffer(ID3D11ShaderReflectionConstantBuffer* constantBuffer);
- void ReflectOverVariable(ID3D11ShaderReflectionVariable* variable);
- void ReflectOverShaderLinkingFunction();
+ static void ReflectOverShader(std::shared_ptr const& outputDescription);
+ static void ReflectOverBindings(std::shared_ptr const& outputDescription, ID3D11ShaderReflection* reflector, D3D11_SHADER_DESC const& desc);
+ static void ReflectOverConstantBuffer(std::shared_ptr const& outputDescription, ID3D11ShaderReflectionConstantBuffer* constantBuffer);
+ static void ReflectOverVariable(std::shared_ptr const& outputDescription, ID3D11ShaderReflectionVariable* variable);
+ static void ReflectOverShaderLinkingFunction(std::shared_ptr const& outputDescription);
};
}}}}}
diff --git a/winrt/lib/utils/Strings.inl b/winrt/lib/utils/Strings.inl
index c03d5c5c6..2e5cb4826 100644
--- a/winrt/lib/utils/Strings.inl
+++ b/winrt/lib/utils/Strings.inl
@@ -37,6 +37,7 @@ STRING(CustomEffectUnknownProperty, L"Shader does not have a property named '%s'
STRING(CustomEffectWrongPropertyArraySize, L"Wrong array size. Shader property '%s' is an array of %d elements.")
STRING(CustomEffectWrongPropertyType, L"Wrong type. Shader property '%s' is of type %s.")
STRING(CustomEffectWrongPropertyTypeArray, L"Wrong type. Shader property '%s' is an array of %s.")
+STRING(CustomEffectNotRegistered, L"The effect is not registered.")
STRING(DeviceExpectedToBeLost, L"This API was unexpectedly called when the Direct3D device is not lost.")
STRING(DidNotPopLayer, L"After calling CanvasDrawingSession.CreateLayer, you must close the resulting CanvasActiveLayer before ending the CanvasDrawingSession.")
STRING(DrawImageMinBlendNotSupported, L"This DrawImage overload is not valid when CanvasDrawingSession.Blend is set to CanvasBlend.Min.")
diff --git a/winrt/test.internal/graphics/PixelShaderEffectUnitTests.cpp b/winrt/test.internal/graphics/PixelShaderEffectUnitTests.cpp
index 27ba7d690..d8e26f376 100644
--- a/winrt/test.internal/graphics/PixelShaderEffectUnitTests.cpp
+++ b/winrt/test.internal/graphics/PixelShaderEffectUnitTests.cpp
@@ -19,7 +19,7 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework;
static ComPtr MakeSharedShaderState(
- ShaderDescription const& shader = ShaderDescription(),
+ std::shared_ptr const& shader = std::make_shared(),
std::vector const& constants = std::vector(),
CoordinateMappingState const& coordinateMapping = CoordinateMappingState(),
SourceInterpolationState const& sourceInterpolation = SourceInterpolationState())
@@ -148,7 +148,7 @@ TEST_CLASS(PixelShaderEffectUnitTests)
sourceInterpolation.Filter[0] = D2D1_FILTER_MIN_MAG_MIP_POINT;
sourceInterpolation.Filter[7] = D2D1_FILTER_ANISOTROPIC;
- auto sharedState = MakeSharedShaderState(ShaderDescription(), constants, coordinateMapping, sourceInterpolation);
+ auto sharedState = MakeSharedShaderState(std::make_shared(), constants, coordinateMapping, sourceInterpolation);
auto effect = Make(nullptr, nullptr, sharedState.Get());
// Realize the effect.
@@ -181,8 +181,8 @@ TEST_CLASS(PixelShaderEffectUnitTests)
ShaderVariable variable(variableDesc, variableType);
- ShaderDescription desc;
- desc.Variables.push_back(variable);
+ std::shared_ptr desc = std::make_shared();
+ desc->Variables.push_back(variable);
auto sharedState = MakeSharedShaderState(desc);
@@ -202,7 +202,7 @@ TEST_CLASS(PixelShaderEffectUnitTests)
*reinterpret_cast(data) = CLSID_PixelShaderEffect;
break;
- case PixelShaderEffectProperty::SharedState:
+ case ((uint32_t)PixelShaderEffectProperty::SharedState):
Assert::AreEqual(sizeof(IUnknown*), dataSize);
*reinterpret_cast(data) = As(sharedState).Detach();
break;
@@ -244,8 +244,8 @@ TEST_CLASS(PixelShaderEffectUnitTests)
ShaderVariable variable(variableDesc, variableType);
- ShaderDescription desc;
- desc.Variables.push_back(variable);
+ std::shared_ptr desc = std::make_shared();
+ desc->Variables.push_back(variable);
auto sharedState = MakeSharedShaderState(desc, std::vector(sizeof(int)));
auto effect = Make(nullptr, nullptr, sharedState.Get());
@@ -575,10 +575,10 @@ TEST_CLASS(PixelShaderEffectImplUnitTests)
Assert::AreEqual(E_FAIL, impl->PrepareForRender(D2D1_CHANGE_TYPE_NONE));
// Set the shared state property.
- ShaderDescription desc;
- desc.Code = { 2, 5, 7, 9, 4 };
- desc.Hash = IID{ 0x8495c8be, 0xd63e, 0x40a0, 0x9f, 0xa5, 0x9, 0x72, 0x4b, 0xaf, 0xf7, 0x15 };
- desc.InputCount = 7;
+ std::shared_ptr desc = std::make_shared();
+ desc->Code = { 2, 5, 7, 9, 4 };
+ desc->Hash = IID{ 0x8495c8be, 0xd63e, 0x40a0, 0x9f, 0xa5, 0x9, 0x72, 0x4b, 0xaf, 0xf7, 0x15 };
+ desc->InputCount = 7;
auto sharedState = MakeSharedShaderState(desc);
@@ -587,18 +587,18 @@ TEST_CLASS(PixelShaderEffectImplUnitTests)
// The first PrepareForRender should call LoadPixelShader and configure the transform graph.
f.MockEffectContext->LoadPixelShaderMethod.SetExpectedCalls(1, [&](IID const& shaderId, BYTE const* shaderBuffer, unsigned shaderBufferCount)
{
- Assert::AreEqual(desc.Hash, shaderId);
- Assert::AreEqual(desc.Code.size(), shaderBufferCount);
+ Assert::AreEqual(desc->Hash, shaderId);
+ Assert::AreEqual(desc->Code.size(), shaderBufferCount);
for (unsigned i = 0; i < shaderBufferCount; i++)
{
- Assert::AreEqual(desc.Code[i], shaderBuffer[i]);
+ Assert::AreEqual(desc->Code[i], shaderBuffer[i]);
}
return S_OK;
});
- f.ExpectTransformGraph(desc.InputCount, 0);
+ f.ExpectTransformGraph(desc->InputCount, 0);
ThrowIfFailed(impl->PrepareForRender(D2D1_CHANGE_TYPE_NONE));
@@ -732,8 +732,8 @@ TEST_CLASS(PixelShaderEffectImplUnitTests)
PixelShaderEffectImpl::Register(f.Factory.Get());
auto& binding = f.FindBinding(L"CoordinateMapping");
- ShaderDescription desc;
- desc.InputCount = 2;
+ std::shared_ptr desc = std::make_shared();
+ desc->InputCount = 2;
auto impl = Make();
@@ -805,7 +805,7 @@ TEST_CLASS(PixelShaderEffectImplUnitTests)
// PrepareForRender should create a graph with no intermediate border transforms.
GraphState graphState;
- f.ExpectTransformGraph(desc.InputCount, 0, mockDrawInfo.Get(), &graphState);
+ f.ExpectTransformGraph(desc->InputCount, 0, mockDrawInfo.Get(), &graphState);
ThrowIfFailed(impl->PrepareForRender(D2D1_CHANGE_TYPE_NONE));
@@ -847,7 +847,7 @@ TEST_CLASS(PixelShaderEffectImplUnitTests)
ThrowIfFailed(binding.setFunction(impl.Get(), reinterpret_cast(&value), sizeof(CoordinateMappingState)));
- f.ExpectTransformGraph(desc.InputCount, 1, mockDrawInfo.Get(), &graphState);
+ f.ExpectTransformGraph(desc->InputCount, 1, mockDrawInfo.Get(), &graphState);
ThrowIfFailed(impl->PrepareForRender(D2D1_CHANGE_TYPE_NONE));
@@ -888,8 +888,8 @@ TEST_CLASS(PixelShaderEffectImplUnitTests)
auto impl = Make();
- ShaderDescription desc;
- desc.InputCount = 2;
+ std::shared_ptr desc = std::make_shared();
+ desc->InputCount = 2;
auto sharedState = MakeSharedShaderState(desc);
ThrowIfFailed(f.FindBinding(L"SharedState").setFunction(impl.Get(), reinterpret_cast(sharedState.GetAddressOf()), sizeof(ISharedShaderState*)));
@@ -936,7 +936,7 @@ TEST_CLASS(PixelShaderEffectImplUnitTests)
// PrepareForRender should pass the interpolation mode through to SetInputDescription.
auto mockDrawInfo = Make();
- f.ExpectTransformGraph(desc.InputCount, 0, mockDrawInfo.Get());
+ f.ExpectTransformGraph(desc->InputCount, 0, mockDrawInfo.Get());
f.MockEffectContext->LoadPixelShaderMethod.SetExpectedCalls(1);
@@ -979,8 +979,8 @@ TEST_CLASS(PixelShaderTransformUnitTests)
public:
TEST_METHOD_EX(PixelShaderTransform_GetInputCount)
{
- ShaderDescription desc;
- desc.InputCount = 5;
+ std::shared_ptr desc = std::make_shared();
+ desc->InputCount = 5;
auto sharedState = MakeSharedShaderState(desc);
auto transform = Make(sharedState.Get(), std::make_shared());
@@ -991,9 +991,9 @@ TEST_CLASS(PixelShaderTransformUnitTests)
TEST_METHOD_EX(PixelShaderTransform_SetDrawInfo)
{
- ShaderDescription desc;
- desc.InstructionCount = 123;
- desc.Hash = IID{ 0x8495c8be, 0xd63e, 0x40a0, 0x9f, 0xa5, 0x9, 0x72, 0x4b, 0xaf, 0xf7, 0x15 };
+ std::shared_ptr desc = std::make_shared();
+ desc->InstructionCount = 123;
+ desc->Hash = IID{ 0x8495c8be, 0xd63e, 0x40a0, 0x9f, 0xa5, 0x9, 0x72, 0x4b, 0xaf, 0xf7, 0x15 };
auto sharedState = MakeSharedShaderState(desc);
auto transform = Make(sharedState.Get(), std::make_shared());
@@ -1003,14 +1003,14 @@ TEST_CLASS(PixelShaderTransformUnitTests)
// SetDrawInfo should call SetPixelShader and SetInstructionCountHint.
mockDrawInfo->SetPixelShaderMethod.SetExpectedCalls(1, [&](IID const& iid, D2D1_PIXEL_OPTIONS options)
{
- Assert::AreEqual(desc.Hash, iid);
+ Assert::AreEqual(desc->Hash, iid);
Assert::AreEqual(D2D1_PIXEL_OPTIONS_NONE, options);
return S_OK;
});
mockDrawInfo->SetInstructionCountHintMethod.SetExpectedCalls(1, [&](UINT32 instructionCount)
{
- Assert::AreEqual(desc.InstructionCount, instructionCount);
+ Assert::AreEqual(desc->InstructionCount, instructionCount);
});
ThrowIfFailed(transform->SetDrawInfo(mockDrawInfo.Get()));
@@ -1099,8 +1099,8 @@ TEST_CLASS(ClipTransformUnitTests)
public:
TEST_METHOD_EX(ClipTransform_GetInputCount)
{
- ShaderDescription desc;
- desc.InputCount = 5;
+ std::shared_ptr desc = std::make_shared();
+ desc->InputCount = 5;
auto sharedState = MakeSharedShaderState(desc);
auto transform = Make(sharedState.Get(), std::make_shared());
@@ -1391,8 +1391,8 @@ TEST_CLASS(SharedShaderStateUnitTests)
TEST_METHOD_EX(SharedShaderState_Clone)
{
- ShaderDescription desc;
- desc.InputCount = 73;
+ std::shared_ptr desc = std::make_shared();
+ desc->InputCount = 73;
std::vector constants = { 29 };
@@ -1404,7 +1404,7 @@ TEST_CLASS(SharedShaderStateUnitTests)
auto originalState = MakeSharedShaderState(desc, constants, coordinateMapping, sourceInterpolation);
- Assert::AreEqual(desc.InputCount, originalState->Shader().InputCount);
+ Assert::AreEqual(desc->InputCount, originalState->Shader()->InputCount);
Assert::AreEqual(1, originalState->Constants().size());
Assert::AreEqual(constants[0], originalState->Constants()[0]);
Assert::AreEqual(coordinateMapping.MaxOffset, originalState->CoordinateMapping().MaxOffset);
@@ -1412,7 +1412,7 @@ TEST_CLASS(SharedShaderStateUnitTests)
auto clone = originalState->Clone();
- Assert::AreEqual(desc.InputCount, clone->Shader().InputCount);
+ Assert::AreEqual(desc->InputCount, clone->Shader()->InputCount);
Assert::AreEqual(1, clone->Constants().size());
Assert::AreEqual(constants[0], clone->Constants()[0]);
Assert::AreEqual(coordinateMapping.MaxOffset, clone->CoordinateMapping().MaxOffset);
@@ -1433,10 +1433,10 @@ TEST_CLASS(SharedShaderStateUnitTests)
auto state2a = Make(compiledShader2.data(), static_cast(compiledShader2.size()));
auto state2b = Make(compiledShader2.data(), static_cast(compiledShader2.size()));
- Assert::AreEqual(state1a->Shader().Hash, state1b->Shader().Hash);
- Assert::AreEqual(state2a->Shader().Hash, state2b->Shader().Hash);
+ Assert::AreEqual(state1a->Shader()->Hash, state1b->Shader()->Hash);
+ Assert::AreEqual(state2a->Shader()->Hash, state2b->Shader()->Hash);
- Assert::AreNotEqual(state1a->Shader().Hash, state2a->Shader().Hash);
+ Assert::AreNotEqual(state1a->Shader()->Hash, state2a->Shader()->Hash);
};
@@ -1444,12 +1444,12 @@ TEST_CLASS(SharedShaderStateUnitTests)
{
auto state = Make(compiledShader1.data(), static_cast(compiledShader1.size()));
- Assert::AreEqual(compiledShader1, state->Shader().Code);
- Assert::AreEqual(0u, state->Shader().InputCount);
- Assert::IsTrue(state->Shader().InstructionCount > 3 && state->Shader().InstructionCount < 20);
- Assert::AreEqual(D3D_FEATURE_LEVEL_10_0, state->Shader().MinFeatureLevel);
+ Assert::AreEqual(compiledShader1, state->Shader()->Code);
+ Assert::AreEqual(0u, state->Shader()->InputCount);
+ Assert::IsTrue(state->Shader()->InstructionCount > 3 && state->Shader()->InstructionCount < 20);
+ Assert::AreEqual(D3D_FEATURE_LEVEL_10_0, state->Shader()->MinFeatureLevel);
- auto& variables = state->Shader().Variables;
+ auto& variables = state->Shader()->Variables;
Assert::AreEqual(7, variables.size());
From 013fa884dd6292118ac384332bb044cd20a08217 Mon Sep 17 00:00:00 2001
From: benstevens48
Date: Fri, 3 Jan 2025 16:18:38 +0000
Subject: [PATCH 02/12] fix path for tests
---
winrt/lib/effects/shader/PixelShaderEffect.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/winrt/lib/effects/shader/PixelShaderEffect.h b/winrt/lib/effects/shader/PixelShaderEffect.h
index 695ac96b3..559c5ddcd 100644
--- a/winrt/lib/effects/shader/PixelShaderEffect.h
+++ b/winrt/lib/effects/shader/PixelShaderEffect.h
@@ -4,7 +4,7 @@
#pragma once
-#include "Utils/GuidUtilities.h"
+#include "../../utils/GuidUtilities.h"
#include "ShaderDescription.h"
namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { namespace Effects
From 7dad435392182e7c90d5497491fc7dc3a88e660e Mon Sep 17 00:00:00 2001
From: benstevens48
Date: Fri, 3 Jan 2025 16:25:58 +0000
Subject: [PATCH 03/12] add test
---
winrt/test.managed/PixelShaderEffectTests.cs | 24 ++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/winrt/test.managed/PixelShaderEffectTests.cs b/winrt/test.managed/PixelShaderEffectTests.cs
index f4daec61a..fe6c96a16 100644
--- a/winrt/test.managed/PixelShaderEffectTests.cs
+++ b/winrt/test.managed/PixelShaderEffectTests.cs
@@ -10,6 +10,9 @@
using System.Linq;
using System.Runtime.InteropServices;
using Windows.Foundation;
+using System.Xml.Linq;
+using Windows.UI.Xaml.Controls;
+
#if WINDOWS_UWP
using System.Numerics;
@@ -318,6 +321,27 @@ float4 main() : SV_Target
}
}
+ [TestMethod]
+ public void PixelShaderEffect_RegisterAndCreateEffect() {
+ const string hlsl =
+ @"
+ float4 color;
+
+ float4 main() : SV_Target
+ {
+ return color;
+ }
+ ";
+
+ var effectId = Guid.Parse("{A686195C-AEA4-45B7-87EF-BDD57776A7F4");
+ var effect = PixelShaderEffect.RegisterAndCreateEffect(ShaderCompiler.CompileShader(hlsl, "ps_4_0"), effectId);
+
+ using (var canvasDevice = new CanvasDevice())
+ using (var renderTarget = new CanvasRenderTarget(canvasDevice, 1, 1, 96))
+ using (var drawingSession = renderTarget.CreateDrawingSession()) {
+ drawingSession.DrawImage(effect);
+ }
+ }
[TestMethod]
public void PixelShaderEffect_PropertiesDictionary_Methods()
From be68101f86666e54962bb9dd33425e7a8343c5ae Mon Sep 17 00:00:00 2001
From: benstevens48
Date: Fri, 3 Jan 2025 16:28:58 +0000
Subject: [PATCH 04/12] remove unecessary include
---
winrt/lib/text/CanvasFontFace.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/winrt/lib/text/CanvasFontFace.cpp b/winrt/lib/text/CanvasFontFace.cpp
index 7e0726d99..39efe922b 100644
--- a/winrt/lib/text/CanvasFontFace.cpp
+++ b/winrt/lib/text/CanvasFontFace.cpp
@@ -6,7 +6,6 @@
#include "CanvasFontFace.h"
#include "TextUtilities.h"
-#include "effects/shader/PixelShaderEffect.h"
#include "DrawGlyphRunHelper.h"
using namespace ABI::Microsoft::Graphics::Canvas;
From 8bd38dff2a5b6a707b9415c3a275ec2554e8dca3 Mon Sep 17 00:00:00 2001
From: benstevens48
Date: Fri, 3 Jan 2025 16:53:21 +0000
Subject: [PATCH 05/12] update docs
---
winrt/docsrc/effects/PixelShaderEffect.xml | 25 ++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/winrt/docsrc/effects/PixelShaderEffect.xml b/winrt/docsrc/effects/PixelShaderEffect.xml
index 6b656d600..3eb8e94a8 100644
--- a/winrt/docsrc/effects/PixelShaderEffect.xml
+++ b/winrt/docsrc/effects/PixelShaderEffect.xml
@@ -199,6 +199,31 @@ Licensed under the MIT License. See LICENSE.txt in the project root for license
+
+ Registers the shader code using the given effectId, which must be unique, and performs one-time reflection over the shader code to extract constant property names etc. Does nothing if the effectId is already registered.
+
+
+ Throws an exception if the reflection over the shader code fails. See for more detail about the shader code requirements.
+
+
+
+
+ Initializes a new instance of the PixelShaderEffect class using the shader code which was previously registered using the given effectId.
+
+
+ Throws an exception if not registered.
+
+
+
+
+ Calls then .
+
+
+ Check to see if the given effectId has been registered via .
+
+
+ Removes the cached shader code with the given effectId. Does nothing if not registered.
+
Gets or sets the first input source for the custom pixel shader.
From 0954a6a7693ad4aa20185053bc88e4d5d1ade4f9 Mon Sep 17 00:00:00 2001
From: benstevens48
Date: Fri, 3 Jan 2025 17:29:23 +0000
Subject: [PATCH 06/12] fix docs
---
winrt/docsrc/effects/PixelShaderEffect.xml | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/winrt/docsrc/effects/PixelShaderEffect.xml b/winrt/docsrc/effects/PixelShaderEffect.xml
index 3eb8e94a8..f53c9c92c 100644
--- a/winrt/docsrc/effects/PixelShaderEffect.xml
+++ b/winrt/docsrc/effects/PixelShaderEffect.xml
@@ -199,7 +199,7 @@ Licensed under the MIT License. See LICENSE.txt in the project root for license
-
+
Registers the shader code using the given effectId, which must be unique, and performs one-time reflection over the shader code to extract constant property names etc. Does nothing if the effectId is already registered.
@@ -207,7 +207,7 @@ Licensed under the MIT License. See LICENSE.txt in the project root for license
-
+
Initializes a new instance of the PixelShaderEffect class using the shader code which was previously registered using the given effectId.
@@ -215,13 +215,13 @@ Licensed under the MIT License. See LICENSE.txt in the project root for license
-
- Calls then .
+
+ Calls then .
-
- Check to see if the given effectId has been registered via .
+
+ Check to see if the given effectId has been registered via .
-
+
Removes the cached shader code with the given effectId. Does nothing if not registered.
From 5759b8887c7388d8c71011c32d8a2be2ccfa1d27 Mon Sep 17 00:00:00 2001
From: benstevens48
Date: Fri, 3 Jan 2025 19:47:33 +0000
Subject: [PATCH 07/12] add other params
---
winrt/docsrc/effects/PixelShaderEffect.xml | 16 ++++++---
.../effects/shader/PixelShaderEffect.abi.idl | 16 ++++++++-
.../lib/effects/shader/PixelShaderEffect.cpp | 17 +++++++---
winrt/lib/effects/shader/PixelShaderEffect.h | 10 ++++--
winrt/lib/effects/shader/ShaderDescription.h | 16 +++++++++
.../lib/effects/shader/SharedShaderState.cpp | 34 +++++++++++++------
winrt/lib/effects/shader/SharedShaderState.h | 11 ++----
winrt/test.managed/PixelShaderEffectTests.cs | 2 +-
8 files changed, 89 insertions(+), 33 deletions(-)
diff --git a/winrt/docsrc/effects/PixelShaderEffect.xml b/winrt/docsrc/effects/PixelShaderEffect.xml
index f53c9c92c..00e4c45e2 100644
--- a/winrt/docsrc/effects/PixelShaderEffect.xml
+++ b/winrt/docsrc/effects/PixelShaderEffect.xml
@@ -199,12 +199,17 @@ Licensed under the MIT License. See LICENSE.txt in the project root for license
-
+
Registers the shader code using the given effectId, which must be unique, and performs one-time reflection over the shader code to extract constant property names etc. Does nothing if the effectId is already registered.
Throws an exception if the reflection over the shader code fails. See for more detail about the shader code requirements.
+
+ The parameter maxSamplerOffset should be set to 0 if the offset coordinate mapping mode is not used. It can be overriden on each instance, although that would be unusual.
+ The coordinateMappings, borderModes and sourceInterpolations parameters are optional and can be set to null or empty or partially specified. The values in the array apply to sources 1 to 8 in order.
+ These properties can be overriden on each instance, although that would be unusual.
+
@@ -215,11 +220,14 @@ Licensed under the MIT License. See LICENSE.txt in the project root for license
-
- Calls then .
+
+
+ Calls
+ then .
+
- Check to see if the given effectId has been registered via .
+ Check to see if the given effectId has been registered via .
Removes the cached shader code with the given effectId. Does nothing if not registered.
diff --git a/winrt/lib/effects/shader/PixelShaderEffect.abi.idl b/winrt/lib/effects/shader/PixelShaderEffect.abi.idl
index bbee3039e..f90c6cc5b 100644
--- a/winrt/lib/effects/shader/PixelShaderEffect.abi.idl
+++ b/winrt/lib/effects/shader/PixelShaderEffect.abi.idl
@@ -20,7 +20,14 @@ namespace Microsoft.Graphics.Canvas.Effects
HRESULT RegisterEffect(
[in] UINT32 shaderCodeCount,
[in, size_is(shaderCodeCount)] BYTE* shaderCode,
- [in] GUID effectId);
+ [in] GUID effectId,
+ [in] INT32 maxSamplerOffset,
+ [in] UINT32 coordinateMappingsSize,
+ [in, size_is(coordinateMappingsSize)] SamplerCoordinateMapping* coordinateMappings,
+ [in] UINT32 borderModesSize,
+ [in, size_is(borderModesSize)] EffectBorderMode* borderModes,
+ [in] UINT32 sourceInterpolationsSize,
+ [in, size_is(sourceInterpolationsSize)] Microsoft.Graphics.Canvas.CanvasImageInterpolation* sourceInterpolations);
HRESULT CreateEffect(
[in] GUID effectId,
@@ -30,6 +37,13 @@ namespace Microsoft.Graphics.Canvas.Effects
[in] UINT32 shaderCodeCount,
[in, size_is(shaderCodeCount)] BYTE* shaderCode,
[in] GUID effectId,
+ [in] INT32 maxSamplerOffset,
+ [in] UINT32 coordinateMappingsSize,
+ [in, size_is(coordinateMappingsSize)] SamplerCoordinateMapping* coordinateMappings,
+ [in] UINT32 borderModesSize,
+ [in, size_is(borderModesSize)] EffectBorderMode* borderModes,
+ [in] UINT32 sourceInterpolationsSize,
+ [in, size_is(sourceInterpolationsSize)] Microsoft.Graphics.Canvas.CanvasImageInterpolation* sourceInterpolations,
[out, retval] PixelShaderEffect** effect);
HRESULT IsEffectRegistered([in] GUID effectId, [out, retval] boolean* result);
diff --git a/winrt/lib/effects/shader/PixelShaderEffect.cpp b/winrt/lib/effects/shader/PixelShaderEffect.cpp
index 6e94b962e..90d0757bb 100644
--- a/winrt/lib/effects/shader/PixelShaderEffect.cpp
+++ b/winrt/lib/effects/shader/PixelShaderEffect.cpp
@@ -31,7 +31,10 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
});
}
- IFACEMETHODIMP PixelShaderEffectFactory::RegisterEffect(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId)
+ IFACEMETHODIMP PixelShaderEffectFactory::RegisterEffect(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId, int32_t maxSamplerOffset,
+ uint32_t coordinateMappingsSize, SamplerCoordinateMapping* coordinateMappings,
+ uint32_t borderModesSize, EffectBorderMode* borderModes,
+ uint32_t sourceInterpolationsSize, CanvasImageInterpolation* sourceInterpolations)
{
return ExceptionBoundary([&]
{
@@ -48,7 +51,8 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
}
CheckInPointer(shaderCode);
- auto description = SharedShaderState::CreateShaderDescription(shaderCode, shaderCodeCount, effectId);
+ auto description = SharedShaderState::CreateShaderDescription(shaderCode, shaderCodeCount, effectId, maxSamplerOffset,
+ coordinateMappings, coordinateMappingsSize, borderModes, borderModesSize, sourceInterpolations, sourceInterpolationsSize);
{
std::lock_guard lock(m_mutex);
@@ -81,7 +85,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
}
// Create a shared state object using the specified shader code.
- auto sharedState = Make(description, description->DefaultConstants, description->DefaultCoordinateMapping, SourceInterpolationState());
+ auto sharedState = Make(description, description->DefaultConstants, description->DefaultCoordinateMapping, description->DefaultSourceInterpolation);
CheckMakeResult(sharedState);
// Create the WinRT effect instance.
@@ -92,11 +96,14 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
});
}
- IFACEMETHODIMP PixelShaderEffectFactory::RegisterAndCreateEffect(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId, IPixelShaderEffect** effect)
+ IFACEMETHODIMP PixelShaderEffectFactory::RegisterAndCreateEffect(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId, int32_t maxSamplerOffset,
+ uint32_t coordinateMappingsSize, SamplerCoordinateMapping* coordinateMappings,
+ uint32_t borderModesSize, EffectBorderMode* borderModes,
+ uint32_t sourceInterpolationsSize, CanvasImageInterpolation* sourceInterpolations, IPixelShaderEffect** effect)
{
return ExceptionBoundary([&]
{
- ThrowIfFailed(RegisterEffect(shaderCodeCount, shaderCode, effectId));
+ ThrowIfFailed(RegisterEffect(shaderCodeCount, shaderCode, effectId, maxSamplerOffset, coordinateMappingsSize, coordinateMappings, borderModesSize, borderModes, sourceInterpolationsSize, sourceInterpolations));
ThrowIfFailed(CreateEffect(effectId, effect));
});
}
diff --git a/winrt/lib/effects/shader/PixelShaderEffect.h b/winrt/lib/effects/shader/PixelShaderEffect.h
index 559c5ddcd..86f712533 100644
--- a/winrt/lib/effects/shader/PixelShaderEffect.h
+++ b/winrt/lib/effects/shader/PixelShaderEffect.h
@@ -29,11 +29,17 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
//
// IPixelShaderEffectStatics
//
- IFACEMETHOD(RegisterEffect)(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId) override;
+ IFACEMETHOD(RegisterEffect)(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId, int32_t maxSamplerOffset,
+ uint32_t coordinateMappingsSize, SamplerCoordinateMapping* coordinateMappings,
+ uint32_t borderModesSize, EffectBorderMode* borderModes,
+ uint32_t sourceInterpolationsSize, CanvasImageInterpolation* sourceInterpolations) override;
IFACEMETHOD(CreateEffect)(GUID effectId, IPixelShaderEffect** effect) override;
- IFACEMETHOD(RegisterAndCreateEffect)(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId, IPixelShaderEffect** effect) override;
+ IFACEMETHOD(RegisterAndCreateEffect)(UINT32 shaderCodeCount, BYTE* shaderCode, GUID effectId, int32_t maxSamplerOffset,
+ uint32_t coordinateMappingsSize, SamplerCoordinateMapping* coordinateMappings,
+ uint32_t borderModesSize, EffectBorderMode* borderModes,
+ uint32_t sourceInterpolationsSize, CanvasImageInterpolation* sourceInterpolations, IPixelShaderEffect** effect) override;
IFACEMETHOD(IsEffectRegistered)(GUID effectId, boolean* result) override;
diff --git a/winrt/lib/effects/shader/ShaderDescription.h b/winrt/lib/effects/shader/ShaderDescription.h
index a0bc01632..4ad58b853 100644
--- a/winrt/lib/effects/shader/ShaderDescription.h
+++ b/winrt/lib/effects/shader/ShaderDescription.h
@@ -88,6 +88,21 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
};
+ // Configures the filtering mode used to sample shader source textures.
+ struct SourceInterpolationState
+ {
+ SourceInterpolationState()
+ {
+ for (int i = 0; i < MaxShaderInputs; i++)
+ {
+ Filter[i] = D2D1_FILTER_MIN_MAG_MIP_LINEAR;
+ }
+ }
+
+ D2D1_FILTER Filter[MaxShaderInputs];
+ };
+
+
// Copyable struct stores shader program code along with metadata describing how to use the shader.
struct ShaderDescription
{
@@ -113,6 +128,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
std::vector DefaultConstants;
CoordinateMappingState DefaultCoordinateMapping;
+ SourceInterpolationState DefaultSourceInterpolation;
};
}}}}}
diff --git a/winrt/lib/effects/shader/SharedShaderState.cpp b/winrt/lib/effects/shader/SharedShaderState.cpp
index 6b9947c1f..967323333 100644
--- a/winrt/lib/effects/shader/SharedShaderState.cpp
+++ b/winrt/lib/effects/shader/SharedShaderState.cpp
@@ -9,15 +9,6 @@
namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { namespace Effects
{
- SourceInterpolationState::SourceInterpolationState()
- {
- for (int i = 0; i < MaxShaderInputs; i++)
- {
- Filter[i] = D2D1_FILTER_MIN_MAG_MIP_LINEAR;
- }
- }
-
-
SharedShaderState::SharedShaderState(std::shared_ptr const& shader, std::vector const& constants, CoordinateMappingState const& coordinateMapping, SourceInterpolationState const& sourceInterpolation)
: m_shader(shader)
, m_constants(constants)
@@ -33,13 +24,15 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
auto hash = GetVersion5Uuid(salt, shaderCode, shaderCodeSize);
- m_shader = CreateShaderDescription(shaderCode, shaderCodeSize, hash);
+ m_shader = CreateShaderDescription(shaderCode, shaderCodeSize, hash, 0, nullptr, 0, nullptr, 0, nullptr, 0);
m_constants = m_shader->DefaultConstants;
m_coordinateMapping = m_shader->DefaultCoordinateMapping;
+ m_sourceInterpolation = m_shader->DefaultSourceInterpolation;
}
- std::shared_ptr SharedShaderState::CreateShaderDescription(BYTE* shaderCode, uint32_t shaderCodeSize, IID const& effectId)
+ std::shared_ptr SharedShaderState::CreateShaderDescription(BYTE* shaderCode, uint32_t shaderCodeSize, IID const& effectId, int32_t maxSamplerOffset,
+ SamplerCoordinateMapping* coordinateMappings, uint32_t coordinateMappingsSize, EffectBorderMode* borderModes, uint32_t borderModesSize, CanvasImageInterpolation* sourceInterpolations, uint32_t sourceInterpolationsSize)
{
auto shader = std::make_shared();
@@ -50,6 +43,25 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
// Look up shader metadata.
ReflectOverShader(shader);
+ shader->DefaultCoordinateMapping.MaxOffset = maxSamplerOffset;
+ if (coordinateMappings != nullptr) {
+ for (uint32_t i = 0; i < coordinateMappingsSize && i < MaxShaderInputs; i++) {
+ shader->DefaultCoordinateMapping.Mapping[i] = coordinateMappings[i];
+ }
+ }
+ if (borderModes != nullptr) {
+ for (uint32_t i = 0; i < borderModesSize && i < MaxShaderInputs; i++) {
+ shader->DefaultCoordinateMapping.BorderMode[i] = borderModes[i];
+ }
+ }
+ if (sourceInterpolations != nullptr) {
+ for (uint32_t i = 0; i < sourceInterpolationsSize && i < MaxShaderInputs; i++) {
+ auto d2dFilter = ToD2DFilter(sourceInterpolations[i]);
+ if (d2dFilter != D2D1_FILTER_FORCE_DWORD) {
+ shader->DefaultSourceInterpolation.Filter[i] = d2dFilter;
+ }
+ }
+ }
return shader;
}
diff --git a/winrt/lib/effects/shader/SharedShaderState.h b/winrt/lib/effects/shader/SharedShaderState.h
index d0a7bb449..a41f100ed 100644
--- a/winrt/lib/effects/shader/SharedShaderState.h
+++ b/winrt/lib/effects/shader/SharedShaderState.h
@@ -12,14 +12,6 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
enum class CopyDirection { Read, Write };
- // Configures the filtering mode used to sample shader source textures.
- struct SourceInterpolationState
- {
- SourceInterpolationState();
-
- D2D1_FILTER Filter[MaxShaderInputs];
- };
-
// Implementation state shared between PixelShaderEffect and PixelShaderEffectImpl.
// This stores the compiled shader code, metadata obtained via shader reflection,
@@ -62,7 +54,8 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
public:
SharedShaderState(std::shared_ptr const& shader, std::vector const& constants, CoordinateMappingState const& coordinateMapping, SourceInterpolationState const& sourceInterpolation);
SharedShaderState(BYTE* shaderCode, uint32_t shaderCodeSize);
- static std::shared_ptr SharedShaderState::CreateShaderDescription(BYTE* shaderCode, uint32_t shaderCodeSize, IID const& effectId);
+ static std::shared_ptr SharedShaderState::CreateShaderDescription(BYTE* shaderCode, uint32_t shaderCodeSize, IID const& effectId, int32_t maxSamplerOffset,
+ SamplerCoordinateMapping* coordinateMappings, uint32_t coordinateMappingsSize, EffectBorderMode* borderModes, uint32_t borderModesSize, CanvasImageInterpolation* sourceInterpolations, uint32_t sourceInterpolationsSize);
virtual ComPtr Clone() override;
diff --git a/winrt/test.managed/PixelShaderEffectTests.cs b/winrt/test.managed/PixelShaderEffectTests.cs
index fe6c96a16..fae9c3d25 100644
--- a/winrt/test.managed/PixelShaderEffectTests.cs
+++ b/winrt/test.managed/PixelShaderEffectTests.cs
@@ -334,7 +334,7 @@ float4 main() : SV_Target
";
var effectId = Guid.Parse("{A686195C-AEA4-45B7-87EF-BDD57776A7F4");
- var effect = PixelShaderEffect.RegisterAndCreateEffect(ShaderCompiler.CompileShader(hlsl, "ps_4_0"), effectId);
+ var effect = PixelShaderEffect.RegisterAndCreateEffect(ShaderCompiler.CompileShader(hlsl, "ps_4_0"), effectId, 0, null, null, null);
using (var canvasDevice = new CanvasDevice())
using (var renderTarget = new CanvasRenderTarget(canvasDevice, 1, 1, 96))
From 3963a60f5240204de2f9bca27aa22d4a70877b3c Mon Sep 17 00:00:00 2001
From: benstevens48
Date: Sat, 4 Jan 2025 08:11:25 +0000
Subject: [PATCH 08/12] minor doc update
---
winrt/docsrc/effects/PixelShaderEffect.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/winrt/docsrc/effects/PixelShaderEffect.xml b/winrt/docsrc/effects/PixelShaderEffect.xml
index 00e4c45e2..750afeda6 100644
--- a/winrt/docsrc/effects/PixelShaderEffect.xml
+++ b/winrt/docsrc/effects/PixelShaderEffect.xml
@@ -206,9 +206,9 @@ Licensed under the MIT License. See LICENSE.txt in the project root for license
Throws an exception if the reflection over the shader code fails. See for more detail about the shader code requirements.
- The parameter maxSamplerOffset should be set to 0 if the offset coordinate mapping mode is not used. It can be overriden on each instance, although that would be unusual.
+ The parameter maxSamplerOffset should be set to 0 if the offset coordinate mapping mode is not used. It can be overriden on each instance.
The coordinateMappings, borderModes and sourceInterpolations parameters are optional and can be set to null or empty or partially specified. The values in the array apply to sources 1 to 8 in order.
- These properties can be overriden on each instance, although that would be unusual.
+ These properties can be overriden on each instance.
From 4122951f27ab3d9a32dd319ab57c977cfe4ba29a Mon Sep 17 00:00:00 2001
From: benstevens48
Date: Sat, 4 Jan 2025 14:38:44 +0000
Subject: [PATCH 09/12] fix doc typo
---
winrt/docsrc/effects/PixelShaderEffect.xml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/winrt/docsrc/effects/PixelShaderEffect.xml b/winrt/docsrc/effects/PixelShaderEffect.xml
index 750afeda6..371913aa3 100644
--- a/winrt/docsrc/effects/PixelShaderEffect.xml
+++ b/winrt/docsrc/effects/PixelShaderEffect.xml
@@ -206,9 +206,9 @@ Licensed under the MIT License. See LICENSE.txt in the project root for license
Throws an exception if the reflection over the shader code fails. See for more detail about the shader code requirements.
- The parameter maxSamplerOffset should be set to 0 if the offset coordinate mapping mode is not used. It can be overriden on each instance.
+ The parameter maxSamplerOffset should be set to 0 if the offset coordinate mapping mode is not used. It can be overridden on each instance.
The coordinateMappings, borderModes and sourceInterpolations parameters are optional and can be set to null or empty or partially specified. The values in the array apply to sources 1 to 8 in order.
- These properties can be overriden on each instance.
+ These properties can be overridden on each instance.
@@ -227,7 +227,7 @@ Licensed under the MIT License. See LICENSE.txt in the project root for license
- Check to see if the given effectId has been registered via .
+ Checks to see if the given effectId has been registered via .
Removes the cached shader code with the given effectId. Does nothing if not registered.
From f33bd6295f346e97dd6a5f5c25e8007bbf57bd0e Mon Sep 17 00:00:00 2001
From: benstevens48
Date: Thu, 9 Jan 2025 08:52:26 +0000
Subject: [PATCH 10/12] prevent unnecessary copy of constants
---
.../lib/effects/shader/PixelShaderEffect.cpp | 9 +-
winrt/lib/effects/shader/PixelShaderEffect.h | 3 +-
winrt/lib/effects/shader/ShaderDescription.h | 39 --------
.../lib/effects/shader/SharedShaderState.cpp | 88 ++++++++++++-------
winrt/lib/effects/shader/SharedShaderState.h | 53 +++++++++--
5 files changed, 107 insertions(+), 85 deletions(-)
diff --git a/winrt/lib/effects/shader/PixelShaderEffect.cpp b/winrt/lib/effects/shader/PixelShaderEffect.cpp
index 90d0757bb..204bd94c9 100644
--- a/winrt/lib/effects/shader/PixelShaderEffect.cpp
+++ b/winrt/lib/effects/shader/PixelShaderEffect.cpp
@@ -5,7 +5,6 @@
#include "pch.h"
#include "PixelShaderEffect.h"
#include "PixelShaderEffectImpl.h"
-#include "SharedShaderState.h"
namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { namespace Effects
{
@@ -71,7 +70,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
{
CheckAndClearOutPointer(effect);
- std::shared_ptr description{ nullptr };
+ ShaderDescriptionWithDefaults description{};
{
std::lock_guard lock(m_mutex);
auto const& cached = shaderCache.find(effectId);
@@ -80,12 +79,12 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
}
}
- if (description == nullptr) {
+ if (description.Description == nullptr || description.Defaults == nullptr) {
ThrowHR(E_INVALIDARG, Strings::CustomEffectNotRegistered);
}
// Create a shared state object using the specified shader code.
- auto sharedState = Make(description, description->DefaultConstants, description->DefaultCoordinateMapping, description->DefaultSourceInterpolation);
+ auto sharedState = Make(description.Description, description.Defaults->DefaultConstants, description.Defaults->DefaultCoordinateMapping, description.Defaults->DefaultSourceInterpolation);
CheckMakeResult(sharedState);
// Create the WinRT effect instance.
@@ -130,7 +129,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
}
std::recursive_mutex PixelShaderEffectFactory::m_mutex{};
- std::map, GuidComparer> PixelShaderEffectFactory::shaderCache{};
+ std::map PixelShaderEffectFactory::shaderCache{};
// Describe how to implement WinRT IMap<> methods in terms of our shader constant buffer.
diff --git a/winrt/lib/effects/shader/PixelShaderEffect.h b/winrt/lib/effects/shader/PixelShaderEffect.h
index 86f712533..a4b712d90 100644
--- a/winrt/lib/effects/shader/PixelShaderEffect.h
+++ b/winrt/lib/effects/shader/PixelShaderEffect.h
@@ -6,6 +6,7 @@
#include "../../utils/GuidUtilities.h"
#include "ShaderDescription.h"
+#include "SharedShaderState.h"
namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { namespace Effects
{
@@ -47,7 +48,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
private:
static std::recursive_mutex m_mutex;
- static std::map, GuidComparer> shaderCache;
+ static std::map shaderCache;
};
diff --git a/winrt/lib/effects/shader/ShaderDescription.h b/winrt/lib/effects/shader/ShaderDescription.h
index 4ad58b853..87532d39d 100644
--- a/winrt/lib/effects/shader/ShaderDescription.h
+++ b/winrt/lib/effects/shader/ShaderDescription.h
@@ -67,41 +67,6 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
bool operator() (HSTRING value1, ShaderVariable const& value2) const { return comparison(value1, value2.Name); }
};
- const int MaxShaderInputs = 8;
-
- // Describes how this shader maps between its input images and output locations.
- struct CoordinateMappingState
- {
- CoordinateMappingState()
- : MaxOffset(0)
- {
- for (int i = 0; i < MaxShaderInputs; i++)
- {
- Mapping[i] = SamplerCoordinateMapping::Unknown;
- BorderMode[i] = EffectBorderMode::Soft;
- }
- }
-
- SamplerCoordinateMapping Mapping[MaxShaderInputs];
- EffectBorderMode BorderMode[MaxShaderInputs];
- int MaxOffset;
- };
-
-
- // Configures the filtering mode used to sample shader source textures.
- struct SourceInterpolationState
- {
- SourceInterpolationState()
- {
- for (int i = 0; i < MaxShaderInputs; i++)
- {
- Filter[i] = D2D1_FILTER_MIN_MAG_MIP_LINEAR;
- }
- }
-
- D2D1_FILTER Filter[MaxShaderInputs];
- };
-
// Copyable struct stores shader program code along with metadata describing how to use the shader.
struct ShaderDescription
@@ -125,10 +90,6 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
// Sorted by name.
std::vector Variables;
-
- std::vector DefaultConstants;
- CoordinateMappingState DefaultCoordinateMapping;
- SourceInterpolationState DefaultSourceInterpolation;
};
}}}}}
diff --git a/winrt/lib/effects/shader/SharedShaderState.cpp b/winrt/lib/effects/shader/SharedShaderState.cpp
index 967323333..1a3da7d99 100644
--- a/winrt/lib/effects/shader/SharedShaderState.cpp
+++ b/winrt/lib/effects/shader/SharedShaderState.cpp
@@ -8,6 +8,25 @@
namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { namespace Effects
{
+ CoordinateMappingState::CoordinateMappingState()
+ : MaxOffset(0)
+ {
+ for (int i = 0; i < MaxShaderInputs; i++)
+ {
+ Mapping[i] = SamplerCoordinateMapping::Unknown;
+ BorderMode[i] = EffectBorderMode::Soft;
+ }
+ }
+
+
+ SourceInterpolationState::SourceInterpolationState()
+ {
+ for (int i = 0; i < MaxShaderInputs; i++)
+ {
+ Filter[i] = D2D1_FILTER_MIN_MAG_MIP_LINEAR;
+ }
+ }
+
SharedShaderState::SharedShaderState(std::shared_ptr const& shader, std::vector const& constants, CoordinateMappingState const& coordinateMapping, SourceInterpolationState const& sourceInterpolation)
: m_shader(shader)
@@ -24,41 +43,44 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
auto hash = GetVersion5Uuid(salt, shaderCode, shaderCodeSize);
- m_shader = CreateShaderDescription(shaderCode, shaderCodeSize, hash, 0, nullptr, 0, nullptr, 0, nullptr, 0);
- m_constants = m_shader->DefaultConstants;
- m_coordinateMapping = m_shader->DefaultCoordinateMapping;
- m_sourceInterpolation = m_shader->DefaultSourceInterpolation;
+ auto shaderWithDefaults = CreateShaderDescription(shaderCode, shaderCodeSize, hash, 0, nullptr, 0, nullptr, 0, nullptr, 0);
+ m_shader = shaderWithDefaults.Description;
+ m_constants.swap(shaderWithDefaults.Defaults->DefaultConstants); //Swap instead of copy since this may be relatively large and we are discarding shaderWithDefaults.Defaults at the end of this function
+ m_coordinateMapping = shaderWithDefaults.Defaults->DefaultCoordinateMapping;
+ m_sourceInterpolation = shaderWithDefaults.Defaults->DefaultSourceInterpolation;
}
- std::shared_ptr SharedShaderState::CreateShaderDescription(BYTE* shaderCode, uint32_t shaderCodeSize, IID const& effectId, int32_t maxSamplerOffset,
+ ShaderDescriptionWithDefaults SharedShaderState::CreateShaderDescription(BYTE* shaderCode, uint32_t shaderCodeSize, IID const& effectId, int32_t maxSamplerOffset,
SamplerCoordinateMapping* coordinateMappings, uint32_t coordinateMappingsSize, EffectBorderMode* borderModes, uint32_t borderModesSize, CanvasImageInterpolation* sourceInterpolations, uint32_t sourceInterpolationsSize)
{
- auto shader = std::make_shared();
+ auto shader = ShaderDescriptionWithDefaults{};
+ shader.Description = std::make_shared();
+ shader.Defaults = std::make_shared();
// Store the shader program code.
- shader->Code.assign(shaderCode, shaderCode + shaderCodeSize);
+ shader.Description->Code.assign(shaderCode, shaderCode + shaderCodeSize);
- shader->Hash = effectId;
+ shader.Description->Hash = effectId;
// Look up shader metadata.
ReflectOverShader(shader);
- shader->DefaultCoordinateMapping.MaxOffset = maxSamplerOffset;
+ shader.Defaults->DefaultCoordinateMapping.MaxOffset = maxSamplerOffset;
if (coordinateMappings != nullptr) {
for (uint32_t i = 0; i < coordinateMappingsSize && i < MaxShaderInputs; i++) {
- shader->DefaultCoordinateMapping.Mapping[i] = coordinateMappings[i];
+ shader.Defaults->DefaultCoordinateMapping.Mapping[i] = coordinateMappings[i];
}
}
if (borderModes != nullptr) {
for (uint32_t i = 0; i < borderModesSize && i < MaxShaderInputs; i++) {
- shader->DefaultCoordinateMapping.BorderMode[i] = borderModes[i];
+ shader.Defaults->DefaultCoordinateMapping.BorderMode[i] = borderModes[i];
}
}
if (sourceInterpolations != nullptr) {
for (uint32_t i = 0; i < sourceInterpolationsSize && i < MaxShaderInputs; i++) {
auto d2dFilter = ToD2DFilter(sourceInterpolations[i]);
if (d2dFilter != D2D1_FILTER_FORCE_DWORD) {
- shader->DefaultSourceInterpolation.Filter[i] = d2dFilter;
+ shader.Defaults->DefaultSourceInterpolation.Filter[i] = d2dFilter;
}
}
}
@@ -476,12 +498,12 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
}
- void SharedShaderState::ReflectOverShader(std::shared_ptr const& outputDescription)
+ void SharedShaderState::ReflectOverShader(ShaderDescriptionWithDefaults const& output)
{
// Create the shader reflection interface.
ComPtr reflector;
- HRESULT hr = D3DReflect(outputDescription->Code.data(), outputDescription->Code.size(), IID_PPV_ARGS(&reflector));
+ HRESULT hr = D3DReflect(output.Description->Code.data(), output.Description->Code.size(), IID_PPV_ARGS(&reflector));
if (FAILED(hr))
ThrowHR(E_INVALIDARG, Strings::CustomEffectBadShader);
@@ -500,25 +522,25 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
}
// Examine the input bindings.
- ReflectOverBindings(outputDescription, reflector.Get(), desc);
+ ReflectOverBindings(output, reflector.Get(), desc);
// Store the mapping from named constants to buffer locations.
if (desc.ConstantBuffers)
{
- ReflectOverConstantBuffer(outputDescription, reflector->GetConstantBufferByIndex(0));
+ ReflectOverConstantBuffer(output, reflector->GetConstantBufferByIndex(0));
}
// Grab some other metadata.
- outputDescription->InstructionCount = desc.InstructionCount;
+ output.Description->InstructionCount = desc.InstructionCount;
- ThrowIfFailed(reflector->GetMinFeatureLevel(&outputDescription->MinFeatureLevel));
+ ThrowIfFailed(reflector->GetMinFeatureLevel(&output.Description->MinFeatureLevel));
// If this shader was compiled to support shader linking, we can also determine which inputs are simple vs. complex.
- ReflectOverShaderLinkingFunction(outputDescription);
+ ReflectOverShaderLinkingFunction(output);
}
- void SharedShaderState::ReflectOverBindings(std::shared_ptr const& outputDescription, ID3D11ShaderReflection* reflector, D3D11_SHADER_DESC const& desc)
+ void SharedShaderState::ReflectOverBindings(ShaderDescriptionWithDefaults const& output, ID3D11ShaderReflection* reflector, D3D11_SHADER_DESC const& desc)
{
for (unsigned i = 0; i < desc.BoundResources; i++)
{
@@ -532,7 +554,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
ThrowHR(E_INVALIDARG, Strings::CustomEffectTooManyTextures);
// Record how many input textures this shader uses.
- outputDescription->InputCount = std::max(outputDescription->InputCount, inputDesc.BindPoint + 1);
+ output.Description->InputCount = std::max(output.Description->InputCount, inputDesc.BindPoint + 1);
break;
case D3D_SIT_CBUFFER:
@@ -545,24 +567,24 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
}
- void SharedShaderState::ReflectOverConstantBuffer(std::shared_ptr const& outputDescription, ID3D11ShaderReflectionConstantBuffer* constantBuffer)
+ void SharedShaderState::ReflectOverConstantBuffer(ShaderDescriptionWithDefaults const& output, ID3D11ShaderReflectionConstantBuffer* constantBuffer)
{
D3D11_SHADER_BUFFER_DESC desc;
ThrowIfFailed(constantBuffer->GetDesc(&desc));
// Resize our constant buffer to match the shader.
- outputDescription->DefaultConstants.resize(desc.Size);
+ output.Defaults->DefaultConstants.resize(desc.Size);
// Look up variable metadata.
- outputDescription->Variables.reserve(desc.Variables);
+ output.Description->Variables.reserve(desc.Variables);
for (unsigned i = 0; i < desc.Variables; i++)
{
- ReflectOverVariable(outputDescription, constantBuffer->GetVariableByIndex(i));
+ ReflectOverVariable(output, constantBuffer->GetVariableByIndex(i));
}
// Sort the variables by name.
- std::sort(outputDescription->Variables.begin(), outputDescription->Variables.end(), VariableNameComparison());
+ std::sort(output.Description->Variables.begin(), output.Description->Variables.end(), VariableNameComparison());
}
@@ -645,7 +667,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
}
- void SharedShaderState::ReflectOverVariable(std::shared_ptr const& outputDescription, ID3D11ShaderReflectionVariable* variable)
+ void SharedShaderState::ReflectOverVariable(ShaderDescriptionWithDefaults const& output, ID3D11ShaderReflectionVariable* variable)
{
D3D11_SHADER_VARIABLE_DESC desc;
ThrowIfFailed(variable->GetDesc(&desc));
@@ -668,7 +690,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
// This can only fail if the shader blob is corrupted.
auto endOffset = desc.StartOffset + desc.Size;
- if (endOffset > outputDescription->DefaultConstants.size() || endOffset < desc.StartOffset)
+ if (endOffset > output.Defaults->DefaultConstants.size() || endOffset < desc.StartOffset)
{
ThrowHR(E_UNEXPECTED);
}
@@ -676,15 +698,15 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
// Initialize our constant buffer with the default value of the variable.
if (desc.DefaultValue)
{
- CopyDefaultValue(outputDescription->DefaultConstants.data() + desc.StartOffset, desc, type);
+ CopyDefaultValue(output.Defaults->DefaultConstants.data() + desc.StartOffset, desc, type);
}
// Store metadata about this variable.
- outputDescription->Variables.emplace_back(desc, type);
+ output.Description->Variables.emplace_back(desc, type);
}
- void SharedShaderState::ReflectOverShaderLinkingFunction(std::shared_ptr const& outputDescription)
+ void SharedShaderState::ReflectOverShaderLinkingFunction(ShaderDescriptionWithDefaults const& output)
{
// If this shader was compiled to support shader linking, we can get extra information
// (telling us which inputs are simple vs. complex) from the shader linking function.
@@ -692,7 +714,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
// It's valid to use shaders that don't support linking, so we return on failure rather than throwing.
ComPtr privateData;
- if (FAILED(D3DGetBlobPart(outputDescription->Code.data(), outputDescription->Code.size(), D3D_BLOB_PRIVATE_DATA, 0, &privateData)))
+ if (FAILED(D3DGetBlobPart(output.Description->Code.data(), output.Description->Code.size(), D3D_BLOB_PRIVATE_DATA, 0, &privateData)))
return;
ComPtr reflector;
@@ -727,7 +749,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
else if (strstr(parameterDesc.SemanticName, "INPUT"))
{
// INPUT semantic means a simple input, so select passthrough coordinate mapping mode.
- outputDescription->DefaultCoordinateMapping.Mapping[inputCount++] = SamplerCoordinateMapping::OneToOne;
+ output.Defaults->DefaultCoordinateMapping.Mapping[inputCount++] = SamplerCoordinateMapping::OneToOne;
}
}
}
diff --git a/winrt/lib/effects/shader/SharedShaderState.h b/winrt/lib/effects/shader/SharedShaderState.h
index a41f100ed..48259643a 100644
--- a/winrt/lib/effects/shader/SharedShaderState.h
+++ b/winrt/lib/effects/shader/SharedShaderState.h
@@ -12,6 +12,45 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
enum class CopyDirection { Read, Write };
+ const int MaxShaderInputs = 8;
+
+
+ // Describes how this shader maps between its input images and output locations.
+ struct CoordinateMappingState
+ {
+ CoordinateMappingState();
+
+ SamplerCoordinateMapping Mapping[MaxShaderInputs];
+ EffectBorderMode BorderMode[MaxShaderInputs];
+ int MaxOffset;
+ };
+
+
+ // Configures the filtering mode used to sample shader source textures.
+ struct SourceInterpolationState
+ {
+ SourceInterpolationState();
+
+ D2D1_FILTER Filter[MaxShaderInputs];
+ };
+
+
+ // Primarily used for caching
+ struct SharedShaderStateDefaults
+ {
+ std::vector DefaultConstants;
+ CoordinateMappingState DefaultCoordinateMapping;
+ SourceInterpolationState DefaultSourceInterpolation;
+ };
+
+
+ // Primarily used for caching
+ struct ShaderDescriptionWithDefaults
+ {
+ std::shared_ptr Description;
+ std::shared_ptr Defaults;
+ };
+
// Implementation state shared between PixelShaderEffect and PixelShaderEffectImpl.
// This stores the compiled shader code, metadata obtained via shader reflection,
@@ -54,7 +93,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
public:
SharedShaderState(std::shared_ptr const& shader, std::vector const& constants, CoordinateMappingState const& coordinateMapping, SourceInterpolationState const& sourceInterpolation);
SharedShaderState(BYTE* shaderCode, uint32_t shaderCodeSize);
- static std::shared_ptr SharedShaderState::CreateShaderDescription(BYTE* shaderCode, uint32_t shaderCodeSize, IID const& effectId, int32_t maxSamplerOffset,
+ static ShaderDescriptionWithDefaults SharedShaderState::CreateShaderDescription(BYTE* shaderCode, uint32_t shaderCodeSize, IID const& effectId, int32_t maxSamplerOffset,
SamplerCoordinateMapping* coordinateMappings, uint32_t coordinateMappingsSize, EffectBorderMode* borderModes, uint32_t borderModesSize, CanvasImageInterpolation* sourceInterpolations, uint32_t sourceInterpolationsSize);
virtual ComPtr Clone() override;
@@ -87,12 +126,12 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
void CopyConstantData(ShaderVariable const& variable, TComponent* values);
- // Shader reflection (done at init time).
- static void ReflectOverShader(std::shared_ptr const& outputDescription);
- static void ReflectOverBindings(std::shared_ptr const& outputDescription, ID3D11ShaderReflection* reflector, D3D11_SHADER_DESC const& desc);
- static void ReflectOverConstantBuffer(std::shared_ptr const& outputDescription, ID3D11ShaderReflectionConstantBuffer* constantBuffer);
- static void ReflectOverVariable(std::shared_ptr const& outputDescription, ID3D11ShaderReflectionVariable* variable);
- static void ReflectOverShaderLinkingFunction(std::shared_ptr const& outputDescription);
+ // Shader reflection (done at init time). Note the members of the output must be non-null before calling these functions.
+ static void ReflectOverShader(ShaderDescriptionWithDefaults const& output);
+ static void ReflectOverBindings(ShaderDescriptionWithDefaults const& output, ID3D11ShaderReflection* reflector, D3D11_SHADER_DESC const& desc);
+ static void ReflectOverConstantBuffer(ShaderDescriptionWithDefaults const& output, ID3D11ShaderReflectionConstantBuffer* constantBuffer);
+ static void ReflectOverVariable(ShaderDescriptionWithDefaults const& output, ID3D11ShaderReflectionVariable* variable);
+ static void ReflectOverShaderLinkingFunction(ShaderDescriptionWithDefaults const& output);
};
}}}}}
From ea6b0d8bf950f62a6cbb22671c3090e3cc840cb1 Mon Sep 17 00:00:00 2001
From: benstevens48
Date: Thu, 9 Jan 2025 14:47:50 +0000
Subject: [PATCH 11/12] minor fix
---
winrt/test.managed/PixelShaderEffectTests.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/winrt/test.managed/PixelShaderEffectTests.cs b/winrt/test.managed/PixelShaderEffectTests.cs
index fae9c3d25..b9b37a030 100644
--- a/winrt/test.managed/PixelShaderEffectTests.cs
+++ b/winrt/test.managed/PixelShaderEffectTests.cs
@@ -333,7 +333,7 @@ float4 main() : SV_Target
}
";
- var effectId = Guid.Parse("{A686195C-AEA4-45B7-87EF-BDD57776A7F4");
+ var effectId = Guid.Parse("A686195C-AEA4-45B7-87EF-BDD57776A7F4");
var effect = PixelShaderEffect.RegisterAndCreateEffect(ShaderCompiler.CompileShader(hlsl, "ps_4_0"), effectId, 0, null, null, null);
using (var canvasDevice = new CanvasDevice())
From a6251a9fd026a2a97a8f94a80c3023b657ed022e Mon Sep 17 00:00:00 2001
From: benstevens48
Date: Thu, 9 Jan 2025 20:41:37 +0000
Subject: [PATCH 12/12] Add additonal tests
---
winrt/test.managed/PixelShaderEffectTests.cs | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/winrt/test.managed/PixelShaderEffectTests.cs b/winrt/test.managed/PixelShaderEffectTests.cs
index b9b37a030..2f88a36de 100644
--- a/winrt/test.managed/PixelShaderEffectTests.cs
+++ b/winrt/test.managed/PixelShaderEffectTests.cs
@@ -334,13 +334,24 @@ float4 main() : SV_Target
";
var effectId = Guid.Parse("A686195C-AEA4-45B7-87EF-BDD57776A7F4");
- var effect = PixelShaderEffect.RegisterAndCreateEffect(ShaderCompiler.CompileShader(hlsl, "ps_4_0"), effectId, 0, null, null, null);
+ var shaderBytes = ShaderCompiler.CompileShader(hlsl, "ps_4_0");
+ var effect = PixelShaderEffect.RegisterAndCreateEffect(shaderBytes, effectId, 0, null, null, null);
using (var canvasDevice = new CanvasDevice())
using (var renderTarget = new CanvasRenderTarget(canvasDevice, 1, 1, 96))
using (var drawingSession = renderTarget.CreateDrawingSession()) {
drawingSession.DrawImage(effect);
}
+
+ Assert.IsTrue(PixelShaderEffect.IsEffectRegistered(effectId));
+ PixelShaderEffect.UnregisterEffect(effectId);
+ Assert.IsFalse(PixelShaderEffect.IsEffectRegistered(effectId));
+ PixelShaderEffect.RegisterEffect(shaderBytes, effectId, 1, new SamplerCoordinateMapping[] { SamplerCoordinateMapping.Offset }, new EffectBorderMode[] { EffectBorderMode.Hard }, new CanvasImageInterpolation[] { CanvasImageInterpolation.NearestNeighbor });
+ effect = PixelShaderEffect.CreateEffect(effectId);
+ Assert.AreEqual(effect.MaxSamplerOffset, 1);
+ Assert.AreEqual(effect.Source1Mapping, SamplerCoordinateMapping.Offset);
+ Assert.AreEqual(effect.Source1BorderMode, EffectBorderMode.Hard);
+ Assert.AreEqual(effect.Source1Interpolation, CanvasImageInterpolation.NearestNeighbor);
}
[TestMethod]