public static int MapInvalidRect(PixelShaderEffect * @this, uint inputIndex, RECT invalidInputRect, RECT *invalidOutputRect)
        {
            @this = (PixelShaderEffect *)&((void **)@this)[-1];

            if (@this->d2D1TransformMapperHandle.Target is D2D1TransformMapper d2D1TransformMapper)
            {
                Rectangle invalidInput = invalidInputRect.ToRectangle();
                Rectangle invalidOutput;

                // Handle exceptions once again
                try
                {
                    d2D1TransformMapper.MapInvalidOutput((int)inputIndex, invalidInput, out invalidOutput);
                }
                catch (Exception e)
                {
                    return(e.HResult);
                }

                *invalidOutputRect = invalidOutput.ToRECT();
            }
            else
            {
                // Default mapping
                *invalidOutputRect = @this->inputRect;
            }

            return(S.S_OK);
        }
    /// <summary>
    /// The factory method for <see cref="ID2D1Factory1.RegisterEffectFromString"/>.
    /// </summary>
    /// <param name="shaderId">The <see cref="Guid"/> for the shader.</param>
    /// <param name="numberOfInputs">The number of inputs for the shader.</param>
    /// <param name="bytecode">The shader bytecode.</param>
    /// <param name="bytecodeSize">The size of <paramref name="bytecode"/>.</param>
    /// <param name="d2D1TransformMapper">The <see cref="D2D1TransformMapper"/> instance to use for the effect.</param>
    /// <param name="effectImpl">The resulting effect instance.</param>
    /// <returns>This always returns <c>0</c>.</returns>
    private static int Factory(
        Guid shaderId,
        int numberOfInputs,
        byte *bytecode,
        int bytecodeSize,
        D2D1TransformMapper?d2D1TransformMapper,
        IUnknown **effectImpl)
    {
        PixelShaderEffect * @this = (PixelShaderEffect *)NativeMemory.Alloc((nuint)sizeof(PixelShaderEffect));

        *@this = default;

        @this->lpVtblForID2D1EffectImpl    = VtblForID2D1EffectImpl;
        @this->lpVtblForID2D1DrawTransform = VtblForID2D1DrawTransform;
        @this->referenceCount            = 1;
        @this->shaderId                  = shaderId;
        @this->numberOfInputs            = numberOfInputs;
        @this->bytecode                  = bytecode;
        @this->bytecodeSize              = bytecodeSize;
        @this->d2D1TransformMapperHandle = GCHandle.Alloc(d2D1TransformMapper);

        *effectImpl = (IUnknown *)@this;

        return(S.S_OK);
    }
        public static int SetDrawInfo(PixelShaderEffect * @this, ID2D1DrawInfo *drawInfo)
        {
            @this = (PixelShaderEffect *)&((void **)@this)[-1];

            @this->d2D1DrawInfo = drawInfo;

            return(drawInfo->SetPixelShader(&@this->shaderId));
        }
        public static int PrepareForRender(PixelShaderEffect * @this, D2D1_CHANGE_TYPE changeType)
        {
            if (@this->constantBuffer is not null)
            {
                return(@this->d2D1DrawInfo->SetPixelShaderConstantBuffer(
                           buffer: @this->constantBuffer,
                           bufferCount: (uint)@this->constantBufferSize));
            }

            return(S.S_OK);
        }
        public static int Initialize(PixelShaderEffect * @this, ID2D1EffectContext *effectContext, ID2D1TransformGraph *transformGraph)
        {
            int hresult = effectContext->LoadPixelShader(
                shaderId: &@this->shaderId,
                shaderBuffer: @this->bytecode,
                shaderBufferCount: (uint)@this->bytecodeSize);

            if (Windows.SUCCEEDED(hresult))
            {
                hresult = transformGraph->SetSingleTransformNode((ID2D1TransformNode *)&@this->lpVtblForID2D1DrawTransform);
            }

            return(hresult);
        }
        public static int Initialize(PixelShaderEffect * @this, ID2D1EffectContext *effectContext, ID2D1TransformGraph *transformGraph)
        {
            int hresult = effectContext->LoadPixelShader(
                shaderId: &@this->shaderId,
                shaderBuffer: @this->bytecode,
                shaderBufferCount: (uint)@this->bytecodeSize);

            // If E_INVALIDARG was returned, try to check whether double precision support was requested when not available. This
            // is only done to provide a more helpful error message to callers. If no error was returned, the behavior is the same.
            if (hresult == E.E_INVALIDARG)
            {
                D2D1_FEATURE_DATA_DOUBLES d2D1FeatureDataDoubles = default;

                // If the call failed, just do nothing and return the previous result
                if (!Windows.SUCCEEDED(effectContext->CheckFeatureSupport(D2D1_FEATURE.D2D1_FEATURE_DOUBLES, &d2D1FeatureDataDoubles, (uint)sizeof(D2D1_FEATURE_DATA_DOUBLES))))
                {
                    return(E.E_INVALIDARG);
                }

                // If the context does not support double precision values, check whether the shader requested them
                if (d2D1FeatureDataDoubles.doublePrecisionFloatShaderOps == 0)
                {
                    using ComPtr <ID3D11ShaderReflection> d3D11ShaderReflection = default;

                    // Create the reflection instance, and in case of error just return the previous error like above
                    if (!Windows.SUCCEEDED(DirectX.D3DReflect(
                                               pSrcData: @this->bytecode,
                                               SrcDataSize: (uint)@this->bytecodeSize,
                                               pInterface: Windows.__uuidof <ID3D11ShaderReflection>(),
                                               ppReflector: d3D11ShaderReflection.GetVoidAddressOf())))
                    {
                        return(E.E_INVALIDARG);
                    }

                    // If the shader requires double precision support, return a more descriptive error
                    if ((d3D11ShaderReflection.Get()->GetRequiresFlags() & (D3D.D3D_SHADER_REQUIRES_DOUBLES | D3D.D3D_SHADER_REQUIRES_11_1_DOUBLE_EXTENSIONS)) != 0)
                    {
                        return(D2DERR.D2DERR_INSUFFICIENT_DEVICE_CAPABILITIES);
                    }
                }
            }

            if (Windows.SUCCEEDED(hresult))
            {
                hresult = transformGraph->SetSingleTransformNode((ID2D1TransformNode *)&@this->lpVtblForID2D1DrawTransform);
            }

            return(hresult);
        }
        public static int MapInvalidRect(PixelShaderEffect * @this, uint inputIndex, RECT invalidInputRect, RECT *invalidOutputRect)
        {
            @this = (PixelShaderEffect *)&((void **)@this)[-1];

            if (inputIndex >= (uint)@this->inputCount)
            {
                return(E.E_INVALIDARG);
            }

            if (@this->d2D1TransformMapperHandle.Target is D2D1TransformMapper d2D1TransformMapper)
            {
                Rectangle invalidInput = invalidInputRect.ToRectangle();
                Rectangle invalidOutput;

                // Handle exceptions once again
                try
                {
                    d2D1TransformMapper.MapInvalidOutput((int)inputIndex, invalidInput, out invalidOutput);
                }
                catch (Exception e)
                {
                    return(e.HResult);
                }

                *invalidOutputRect = invalidOutput.ToRECT();
            }
            else
            {
                // The default mapping in this scenario just needs to set the invalid output rect for simple inputs to
                // be the invalid input rect, and to set the invalid output rect for complex inputs to an infinite rect.
                switch (@this->inputTypes[inputIndex])
                {
                case D2D1PixelShaderInputType.Simple:
                    *invalidOutputRect = invalidInputRect;
                    break;

                case D2D1PixelShaderInputType.Complex:
                    invalidOutputRect->MakeD2D1Infinite();
                    break;

                default:
                    return(E.E_FAIL);
                }
            }

            return(S.S_OK);
        }
        public static int MapOutputRectToInputRects(PixelShaderEffect * @this, RECT *outputRect, RECT *inputRects, uint inputRectsCount)
        {
            @this = (PixelShaderEffect *)&((void **)@this)[-1];

            if (inputRectsCount != @this->numberOfInputs)
            {
                return(E.E_INVALIDARG);
            }

            if (@this->d2D1TransformMapperHandle.Target is D2D1TransformMapper d2D1TransformMapper)
            {
                Rectangle        output = outputRect->ToRectangle();
                Span <Rectangle> inputs = stackalloc Rectangle[8].Slice(0, (int)inputRectsCount);

                for (int i = 0; i < (int)inputRectsCount; i++)
                {
                    inputs[i] = inputRects[i].ToRectangle();
                }

                // Invoke MapOutputToInputs and handle exceptions so they don't cross the ABI boundary
                try
                {
                    d2D1TransformMapper.MapOutputToInputs(in output, inputs);
                }
                catch (Exception e)
                {
                    return(e.HResult);
                }

                for (int i = 0; i < (int)inputRectsCount; i++)
                {
                    inputRects[i] = inputs[i].ToRECT();
                }
            }
            else
            {
                // Default mapping
                for (int i = 0; i < (int)inputRectsCount; i++)
                {
                    inputRects[i] = *outputRect;
                }
            }

            return(S.S_OK);
        }
Example #9
0
    public static int SetConstantBuffer(IUnknown *effect, byte *data, uint dataSize)
    {
        PixelShaderEffect * @this = (PixelShaderEffect *)effect;

        if (@this->constantBuffer is not null)
        {
            NativeMemory.Free(@this->constantBuffer);
        }

        void *buffer = NativeMemory.Alloc(dataSize);

        Buffer.MemoryCopy(data, buffer, dataSize, dataSize);

        @this->constantBuffer     = (byte *)buffer;
        @this->constantBufferSize = (int)dataSize;

        return(S.S_OK);
    }
Example #10
0
    public static int GetConstantBuffer(IUnknown *effect, byte *data, uint dataSize, uint *actualSize)
    {
        PixelShaderEffect * @this = (PixelShaderEffect *)effect;

        if (@this->constantBufferSize == 0)
        {
            *actualSize = 0;
        }
        else
        {
            int bytesToCopy = Math.Min((int)dataSize, @this->constantBufferSize);

            Buffer.MemoryCopy(@this->constantBuffer, data, dataSize, bytesToCopy);

            *actualSize = (uint)bytesToCopy;
        }

        return(S.S_OK);
    }
        public static int SetDrawInfo(PixelShaderEffect * @this, ID2D1DrawInfo *drawInfo)
        {
            @this = (PixelShaderEffect *)&((void **)@this)[-1];

            // Free the previous ID2D1DrawInfo object, if present
            if (@this->d2D1DrawInfo is not null)
            {
                _ = @this->d2D1DrawInfo->Release();
            }

            // Store the new ID2D1DrawInfo object
            _ = drawInfo->AddRef();

            @this->d2D1DrawInfo = drawInfo;

            // Set the pixel shader for the effect
            HRESULT hresult = drawInfo->SetPixelShader(&@this->shaderId, (D2D1_PIXEL_OPTIONS)@this->pixelOptions);

            if (hresult != S.S_OK)
            {
                return(hresult);
            }

            // If any input descriptions are present, set them
            if (@this->inputDescriptionCount > 0)
            {
                for (int i = 0; i < @this->inputDescriptionCount; i++)
                {
                    ref D2D1InputDescription inputDescription = ref @this->inputDescriptions[i];

                    D2D1_INPUT_DESCRIPTION d2D1InputDescription;
                    d2D1InputDescription.filter             = (D2D1_FILTER)inputDescription.Filter;
                    d2D1InputDescription.levelOfDetailCount = (uint)inputDescription.LevelOfDetailCount;

                    hresult = drawInfo->SetInputDescription((uint)inputDescription.Index, d2D1InputDescription);

                    if (hresult != S.S_OK)
                    {
                        return(hresult);
                    }
                }
            }
        public static uint Release(PixelShaderEffect * @this)
        {
            @this = (PixelShaderEffect *)&((void **)@this)[-1];

            return(@this->Release());
        }
        public static int MapInputRectsToOutputRect(PixelShaderEffect * @this, RECT *inputRects, RECT *inputOpaqueSubRects, uint inputRectCount, RECT *outputRect, RECT *outputOpaqueSubRect)
        {
            @this = (PixelShaderEffect *)&((void **)@this)[-1];

            if (inputRectCount != @this->inputCount)
            {
                return(E.E_INVALIDARG);
            }

            if (@this->d2D1TransformMapperHandle.Target is D2D1TransformMapper d2D1TransformMapper)
            {
                Span <Rectangle> inputs       = stackalloc Rectangle[8].Slice(0, (int)inputRectCount);
                Span <Rectangle> opaqueInputs = stackalloc Rectangle[8].Slice(0, (int)inputRectCount);

                for (int i = 0; i < (int)inputRectCount; i++)
                {
                    inputs[i]       = inputRects[i].ToRectangle();
                    opaqueInputs[i] = inputOpaqueSubRects[i].ToRectangle();
                }

                ReadOnlySpan <byte> buffer = new(@this->constantBuffer, @this->constantBufferSize);

                Rectangle output;
                Rectangle opaqueOutput;

                // Handle exceptions, as mentioned above
                try
                {
                    d2D1TransformMapper.MapInputsToOutput(buffer, inputs, opaqueInputs, out output, out opaqueOutput);
                }
                catch (Exception e)
                {
                    return(e.HResult);
                }

                *outputRect          = output.ToRECT();
                *outputOpaqueSubRect = opaqueOutput.ToRECT();
            }
            else if (inputRectCount == 0)
            {
                // If there are no inputs, make the output rectangle infinite. This is useful
                // to make output-only effects work fine by default, without needing a custom
                // transform. In this case, the target area will be the output node anyway.
                outputRect->MakeD2D1Infinite();

                *outputOpaqueSubRect = default;
            }
            else
            {
                // If at least one input is present and no custom mapper is available, apply the default
                // mapping. In this case, the output rect should be the union of the input rects for all
                // the simple shader inputs, or infinite if there are no simple inputs. The input rects
                // for complex inputs, if present, are not needed in this scenario, so they're ignored.
                RECT unionOfSimpleRects        = default;
                bool isUnionOfSimpleRectsEmpty = true;

                for (uint i = 0; i < inputRectCount; i++)
                {
                    if (@this->inputTypes[i] == D2D1PixelShaderInputType.Simple)
                    {
                        if (isUnionOfSimpleRectsEmpty)
                        {
                            unionOfSimpleRects        = inputRects[i];
                            isUnionOfSimpleRectsEmpty = false;
                        }
                        else
                        {
                            unionOfSimpleRects = unionOfSimpleRects.Union(inputRects[i]);
                        }
                    }
                }

                if (isUnionOfSimpleRectsEmpty)
                {
                    outputRect->MakeD2D1Infinite();
                }
                else
                {
                    *outputRect = unionOfSimpleRects;
                }

                *outputOpaqueSubRect = default;
            }

            return(S.S_OK);
        }
 public static uint AddRef(PixelShaderEffect * @this)
 {
     return(@this->AddRef());
 }
 public static uint Release(PixelShaderEffect * @this)
 {
     return(@this->Release());
 }
 public static int QueryInterface(PixelShaderEffect * @this, Guid *riid, void **ppvObject)
 {
     return(@this->QueryInterface(riid, ppvObject));
 }
 public static int SetGraph(PixelShaderEffect * @this, ID2D1TransformGraph *transformGraph)
 {
     return(E.E_NOTIMPL);
 }
        public static uint GetInputCount(PixelShaderEffect * @this)
        {
            @this = (PixelShaderEffect *)&((void **)@this)[-1];

            return((uint)@this->numberOfInputs);
        }
        public static uint AddRef(PixelShaderEffect * @this)
        {
            @this = (PixelShaderEffect *)&((void **)@this)[-1];

            return(@this->AddRef());
        }
        public static int MapInputRectsToOutputRect(PixelShaderEffect * @this, RECT *inputRects, RECT *inputOpaqueSubRects, uint inputRectCount, RECT *outputRect, RECT *outputOpaqueSubRect)
        {
            @this = (PixelShaderEffect *)&((void **)@this)[-1];

            if (inputRectCount != @this->numberOfInputs)
            {
                return(E.E_INVALIDARG);
            }

            if (@this->d2D1TransformMapperHandle.Target is D2D1TransformMapper d2D1TransformMapper)
            {
                Span <Rectangle> inputs       = stackalloc Rectangle[8].Slice(0, (int)inputRectCount);
                Span <Rectangle> opaqueInputs = stackalloc Rectangle[8].Slice(0, (int)inputRectCount);

                for (int i = 0; i < (int)inputRectCount; i++)
                {
                    inputs[i]       = inputRects[i].ToRectangle();
                    opaqueInputs[i] = inputOpaqueSubRects[i].ToRectangle();
                }

                ReadOnlySpan <byte> buffer = new(@this->constantBuffer, @this->constantBufferSize);

                Rectangle output;
                Rectangle opaqueOutput;

                // Handle exceptions, as mentioned above
                try
                {
                    d2D1TransformMapper.MapInputsToOutput(buffer, inputs, opaqueInputs, out output, out opaqueOutput);
                }
                catch (Exception e)
                {
                    return(e.HResult);
                }

                *outputRect          = output.ToRECT();
                *outputOpaqueSubRect = opaqueOutput.ToRECT();
            }
            else if (inputRectCount == 0)
            {
                // If there are no inputs, make the output rectangle infinite. This is useful
                // to make output-only effects work fine by default, without needing a custom
                // transform. In this case, the target area will be the output node anyway.
                outputRect->MakeD2D1Infinite();

                @this->inputRect = default;

                *outputOpaqueSubRect = default;
            }
            else
            {
                // Default mapping
                *outputRect = inputRects[0];

                @this->inputRect = inputRects[0];

                *outputOpaqueSubRect = default;
            }

            return(S.S_OK);
        }
        public static int QueryInterface(PixelShaderEffect * @this, Guid *riid, void **ppvObject)
        {
            @this = (PixelShaderEffect *)&((void **)@this)[-1];

            return(@this->QueryInterface(riid, ppvObject));
        }
        public static int MapOutputRectToInputRects(PixelShaderEffect * @this, RECT *outputRect, RECT *inputRects, uint inputRectsCount)
        {
            @this = (PixelShaderEffect *)&((void **)@this)[-1];

            if (inputRectsCount != @this->inputCount)
            {
                return(E.E_INVALIDARG);
            }

            if (@this->d2D1TransformMapperHandle.Target is D2D1TransformMapper d2D1TransformMapper)
            {
                Rectangle        output = outputRect->ToRectangle();
                Span <Rectangle> inputs = stackalloc Rectangle[8].Slice(0, (int)inputRectsCount);

                for (int i = 0; i < (int)inputRectsCount; i++)
                {
                    inputs[i] = inputRects[i].ToRectangle();
                }

                // Invoke MapOutputToInputs and handle exceptions so they don't cross the ABI boundary
                try
                {
                    d2D1TransformMapper.MapOutputToInputs(in output, inputs);
                }
                catch (Exception e)
                {
                    return(e.HResult);
                }

                for (int i = 0; i < (int)inputRectsCount; i++)
                {
                    inputRects[i] = inputs[i].ToRECT();
                }
            }
            else
            {
                // If no custom transform is used, apply the default mapping. In this case, the loop will
                // automatically handle cases where no inputs are defined. If inputs are present instead,
                // the default mapping will set the input rect for a simple input to be the same as the
                // output rect, and the input rect for a complex rect to be infinite. In both cases, D2D
                // will handle clipping the inputs to the actual output rect area, if needed.
                for (uint i = 0; i < inputRectsCount; i++)
                {
                    switch (@this->inputTypes[i])
                    {
                    case D2D1PixelShaderInputType.Simple:
                        inputRects[i] = *outputRect;
                        break;

                    case D2D1PixelShaderInputType.Complex:
                        inputRects[i].MakeD2D1Infinite();
                        break;

                    default:
                        return(E.E_FAIL);
                    }
                }
            }

            return(S.S_OK);
        }