Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pixel shader effect cache #981

Open
wants to merge 13 commits into
base: uwp/main
Choose a base branch
from
33 changes: 33 additions & 0 deletions winrt/docsrc/effects/PixelShaderEffect.xml
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,39 @@ Licensed under the MIT License. See LICENSE.txt in the project root for license
</ul>
</remarks>
</member>
<member name="M:Microsoft.Graphics.Canvas.Effects.PixelShaderEffect.RegisterEffect(System.Byte[],System.Guid,System.Int32,Microsoft.Graphics.Canvas.Effects.SamplerCoordinateMapping[],Microsoft.Graphics.Canvas.Effects.EffectBorderMode[],Microsoft.Graphics.Canvas.CanvasImageInterpolation[])">
<summary>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.</summary>
<remarks>
<p>
Throws an exception if the reflection over the shader code fails. See <see cref="M:Microsoft.Graphics.Canvas.Effects.PixelShaderEffect.#ctor(System.Byte[])"/> for more detail about the shader code requirements.
</p>
<p>
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 overridden on each instance.
</p>
</remarks>
</member>
<member name="M:Microsoft.Graphics.Canvas.Effects.PixelShaderEffect.CreateEffect(System.Guid)">
<summary>Initializes a new instance of the PixelShaderEffect class using the shader code which was previously registered using the given effectId.</summary>
<remarks>
<p>
Throws an exception if not registered.
</p>
</remarks>
</member>
<member name="M:Microsoft.Graphics.Canvas.Effects.PixelShaderEffect.RegisterAndCreateEffect(System.Byte[],System.Guid,System.Int32,Microsoft.Graphics.Canvas.Effects.SamplerCoordinateMapping[],Microsoft.Graphics.Canvas.Effects.EffectBorderMode[],Microsoft.Graphics.Canvas.CanvasImageInterpolation[])">
<summary>
Calls <see cref="M:Microsoft.Graphics.Canvas.Effects.PixelShaderEffect.RegisterEffect(System.Byte[],System.Guid,System.Int32,Microsoft.Graphics.Canvas.Effects.SamplerCoordinateMapping[],Microsoft.Graphics.Canvas.Effects.EffectBorderMode[],Microsoft.Graphics.Canvas.CanvasImageInterpolation[])"/>
then <see cref="M:Microsoft.Graphics.Canvas.Effects.PixelShaderEffect.CreateEffect(System.Guid)"/>.
</summary>
</member>
<member name="M:Microsoft.Graphics.Canvas.Effects.PixelShaderEffect.IsEffectRegistered(System.Guid)">
<summary>Checks to see if the given effectId has been registered via <see cref="M:Microsoft.Graphics.Canvas.Effects.PixelShaderEffect.RegisterEffect(System.Byte[],System.Guid,System.Int32,Microsoft.Graphics.Canvas.Effects.SamplerCoordinateMapping[],Microsoft.Graphics.Canvas.Effects.EffectBorderMode[],Microsoft.Graphics.Canvas.CanvasImageInterpolation[])"/>.</summary>
</member>
<member name="M:Microsoft.Graphics.Canvas.Effects.PixelShaderEffect.UnregisterEffect(System.Guid)">
<summary>Removes the cached shader code with the given effectId. Does nothing if not registered.</summary>
</member>
<member name="P:Microsoft.Graphics.Canvas.Effects.PixelShaderEffect.Source1">
<summary>Gets or sets the first input source for the custom pixel shader.</summary>
</member>
Expand Down
2 changes: 1 addition & 1 deletion winrt/lib/effects/shader/ClipTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}


Expand Down
39 changes: 38 additions & 1 deletion winrt/lib/effects/shader/PixelShaderEffect.abi.idl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,43 @@ 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,
[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,
[out, retval] PixelShaderEffect** effect);

HRESULT RegisterAndCreateEffect(
[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);

HRESULT UnregisterEffect([in] GUID effectId);
};

[version(VERSION), uuid(FC8C3C31-FA96-45E2-8B72-1741C65CEE8E), exclusiveto(PixelShaderEffect)]
interface IPixelShaderEffect : IInspectable
requires ICanvasEffect
Expand Down Expand Up @@ -107,7 +144,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;
Expand Down
112 changes: 106 additions & 6 deletions winrt/lib/effects/shader/PixelShaderEffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -31,6 +30,107 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
});
}

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([&]
{
if (effectId == GUID_NULL) {
ThrowHR(E_INVALIDARG);
}

{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto const& cached = shaderCache.find(effectId);
if (cached != shaderCache.end()) {
return;
}
}

CheckInPointer(shaderCode);
auto description = SharedShaderState::CreateShaderDescription(shaderCode, shaderCodeCount, effectId, maxSamplerOffset,
coordinateMappings, coordinateMappingsSize, borderModes, borderModesSize, sourceInterpolations, sourceInterpolationsSize);

{
std::lock_guard<std::recursive_mutex> 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);

ShaderDescriptionWithDefaults description{};
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto const& cached = shaderCache.find(effectId);
if (cached != shaderCache.end()) {
description = cached->second;
}
}

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<SharedShaderState>(description.Description, description.Defaults->DefaultConstants, description.Defaults->DefaultCoordinateMapping, description.Defaults->DefaultSourceInterpolation);
CheckMakeResult(sharedState);

// Create the WinRT effect instance.
auto newEffect = Make<PixelShaderEffect>(nullptr, nullptr, sharedState.Get());
CheckMakeResult(newEffect);

ThrowIfFailed(newEffect.CopyTo(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, maxSamplerOffset, coordinateMappingsSize, coordinateMappings, borderModesSize, borderModes, sourceInterpolationsSize, sourceInterpolations));
ThrowIfFailed(CreateEffect(effectId, effect));
});
}

IFACEMETHODIMP PixelShaderEffectFactory::IsEffectRegistered(GUID effectId, boolean* result)
{
return ExceptionBoundary([&]
{
CheckInPointer(result);

std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto const& cached = shaderCache.find(effectId);
*result = cached != shaderCache.end();
});
}

IFACEMETHODIMP PixelShaderEffectFactory::UnregisterEffect(GUID effectId)
{
return ExceptionBoundary([&]
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
shaderCache.erase(effectId);
});
}

std::recursive_mutex PixelShaderEffectFactory::m_mutex{};
std::map<GUID, ShaderDescriptionWithDefaults, GuidComparer> PixelShaderEffectFactory::shaderCache{};


// Describe how to implement WinRT IMap<> methods in terms of our shader constant buffer.
template<typename TKey, typename TValue>
Expand Down Expand Up @@ -81,7 +181,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<IPixelShaderEffect*>(this))
: CanvasEffect(CLSID_PixelShaderEffect, 0, sharedState->Shader()->InputCount, true, device, effect, static_cast<IPixelShaderEffect*>(this))
, m_sharedState(sharedState)
{
m_propertyMap = Make<PropertyMap>(true, this);
Expand Down Expand Up @@ -210,7 +310,7 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na
ThrowIfFailed(As<IDirect3DDxgiInterfaceAccess>(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;
}
Expand Down Expand Up @@ -263,7 +363,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;
Expand All @@ -279,14 +379,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());
}
}
Expand Down
32 changes: 31 additions & 1 deletion winrt/lib/effects/shader/PixelShaderEffect.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

#pragma once

#include "../../utils/GuidUtilities.h"
#include "ShaderDescription.h"
#include "SharedShaderState.h"

namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { namespace Effects
{
class ISharedShaderState;
Expand All @@ -12,13 +16,39 @@ namespace ABI { namespace Microsoft { namespace Graphics { namespace Canvas { na


// WinRT activation factory.
class PixelShaderEffectFactory : public AgileActivationFactory<IPixelShaderEffectFactory>
class PixelShaderEffectFactory : public AgileActivationFactory<IPixelShaderEffectFactory, IPixelShaderEffectStatics>
, private LifespanTracker<PixelShaderEffectFactory>
{
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, 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, 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;

IFACEMETHOD(UnregisterEffect)(GUID effectId) override;

private:
static std::recursive_mutex m_mutex;
static std::map<GUID, ShaderDescriptionWithDefaults, GuidComparer> shaderCache;
};


Expand Down
4 changes: 2 additions & 2 deletions winrt/lib/effects/shader/PixelShaderEffectImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<UINT32>(shader.Code.size()));
HRESULT hr = m_effectContext->LoadPixelShader(shader->Hash, shader->Code.data(), static_cast<UINT32>(shader->Code.size()));

if (FAILED(hr))
ThrowHR(hr, Strings::CustomEffectBadShader);
Expand Down Expand Up @@ -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])
{
Expand Down
8 changes: 4 additions & 4 deletions winrt/lib/effects/shader/PixelShaderTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}


Expand Down Expand Up @@ -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);
});
}

Expand All @@ -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++)
{
Expand Down
Loading