Win2D: правильное использование CanvasVirtualControl

Я хочу нарисовать набор Мандельброта, взятый из Win2D-Example-Gallery, и немного его подправить.

Сначала у меня был весь мой код для генерации Мандельброта внутри CreateResources-метода CanvasAnimatedControl, но из-за проблем с производительностью я продолжил использовать шейдеры (HLSL или PixelShaderEffect) и CanvasVirtualControl:

public PixelShaderEffect _effectMandel;
CanvasVirtualImageSource _sdrc;

public async Task CreateResources(CanvasVirtualControl sender)
{
    _sdrc = new CanvasVirtualImageSource(sender, new Size(_width, _height));
    var arr = await FileHelper.ReadAllBytes("Shaders/Mandelbrot.bin");
    if (arr != null)
    {
        _effectMandel = new PixelShaderEffect(arr);

        using (CanvasDrawingSession drawingSession = sender.CreateDrawingSession(new Rect(0,0,_width,_height)))
        {
            drawingSession.DrawImage(_effectMandel);
        }
    }
}

Когда я запускаю приложение, я получаю System.Runtime.InteropServices.COMException прямо в разделе using, и открывается файл App.g.i.cs, сообщающий мне:

Отладчик сталкивается с этим

Я использую такой шейдерный код:

// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Licensed under the MIT License. See LICENSE.txt in the project root for license information.


// This shader has no input textures.
// It generates a mandelbrot fractal.

#define D2D_INPUT_COUNT 0
#define D2D_REQUIRES_SCENE_POSITION

#include "d2d1effecthelpers.hlsli"


float scale;
float2 translate;

static const float4 tapOffsetsX = float4(-0.25,  0.25, -0.25, 0.25);
static const float4 tapOffsetsY = float4(-0.25, -0.25,  0.25, 0.25);

static const int iterations = 100;


D2D_PS_ENTRY(main)
{
    float2 pos = D2DGetScenePosition().xy;

    // Improve visual quality by supersampling inside the pixel shader, evaluating four separate
    // versions of the fractal in parallel, each at a slightly different position offset.
    // The x, y, z, and w components of these float4s contain the four simultaneous computations.
    float4 c_r = (pos.x + tapOffsetsX) * scale + translate.x;
    float4 c_i = (pos.y + tapOffsetsY) * scale + translate.y;

    float4 value_r = 0;
    float4 value_i = 0;

    // Evalulate the Mandelbrot fractal.
    for (int i = 0; i < iterations; i++)
    {
        float4 new_r = value_r * value_r - value_i * value_i + c_r;
        float4 new_i = value_r * value_i * 2 + c_i;

        value_r = new_r;
        value_i = new_i;
    }

    // Adjust our four parallel results to range 0:1.
    float4 distanceSquared = value_r * value_r + value_i * value_i;

    float4 vectorResult = isfinite(distanceSquared) ? saturate(1 - distanceSquared) : 0;

    // Resolve the supersampling to produce a single scalar result.
    float result = dot(vectorResult, 0.25);

    if (result < 1.0 / 256)
        return 0;
    else
        return float4(result, result, result, 1);
}

Если вы знаете, почему это происходит, ответьте. Спасибо!


person Manticore    schedule 06.12.2016    source источник
comment
Возможно, потому, что загруженный вами файл не является скомпилированным шейдером FX? Ознакомьтесь с stackoverflow.com/questions/1938514/   -  person Berin Loritsch    schedule 08.12.2016
comment
@BerinLoritsch Я обновил свой вопрос и предоставил код шейдера от Microsoft. Теперь я последовал приведенному здесь примеру microsoft.github.io/ml/ но тоже не сработало.   -  person Manticore    schedule 08.12.2016
comment
Прошло много времени с тех пор, как я написал код шейдера, и самая большая проблема, с которой я столкнулся, заключалась в том, чтобы убедиться, что двоичный файл соответствует моим ожиданиям. Я не думаю, что скомпилированный код шейдера чувствителен к 32/64 битам. Также убедитесь, что вы очистили все объектные файлы (файл .g.i.cs является сгенерированным исходным файлом). Возможно, вам также придется перезапустить Visual Studio.   -  person Berin Loritsch    schedule 08.12.2016
comment
Оказалось, что мне пришлось активировать свои настройки отладки, как указано в stackoverflow.com/questions/4281425/, и теперь я вижу следующее сообщение об ошибке: CreateDrawingSession cannot be called before the RegionsInvalidated event has been raised.   -  person Manticore    schedule 08.12.2016
comment
Мой код оказался не в том разделе. Я не знал, что мне пришлось вручную настраивать таймер и аннулировать холст. Теперь я это делаю, и все работает нормально.   -  person Manticore    schedule 08.12.2016


Ответы (1)


Мне нужно было настроить таймер, чтобы регулярно отключать холст и получать 60 кадров в секунду. Я еще раз заглянул в примеры Microsoft и, наконец, решил это с помощью этого кода:

DispatcherTimer timer;
internal void Regions_Invalidated(CanvasVirtualControl sender, CanvasRegionsInvalidatedEventArgs args)
{
    // Configure the Mandelbrot effect to position and scale its output. 
    float baseScale = 0.005f;
    float scale = (baseScale * 96 / sender.Dpi) / (helper._modifiers[1] / 1000f);
    var controlSize = baseScale * sender.Size.ToVector2() * scale;
    Vector2 translate = (baseScale * sender.Size.ToVector2() * new Vector2(-0.5f,-0f));

    _effectMandel.Properties["scale"] = scale;
    _effectMandel.Properties["translate"] = (Microsoft.Graphics.Canvas.Numerics.Vector2)translate;
#endif

    // Draw the effect to whatever regions of the CanvasVirtualControl have been invalidated.
    foreach (var region in args.InvalidatedRegions)
    {
        using (var drawingSession = sender.CreateDrawingSession(region))
        {
            drawingSession.DrawImage(_effectMandel);
        }
    }

    // start timer for fps
    this.timer = new DispatcherTimer();
    int fps = 60;
    this.timer.Interval = new TimeSpan(0, 0, 0, 0, 100 / fps);
    this.timer.Tick += timer_Tick;
    this.timer.Start();
}

private void timer_Tick(object sender, object e)
{
    this.timer.Stop();
    _canvas.Invalidate();
}

Надеюсь, это кому-то поможет.

person Manticore    schedule 08.12.2016
comment
Можно использовать CanvasAnimatedControl - person lindexi; 13.12.2018