Wydaje się, że właściwą odpowiedzią na to jest pominięcie ContentPipeline i użycie Texture2D.FromStream do załadowania tekstur w czasie wykonywania. Ta metoda działa dobrze na PC i choć będzie niewielki wzrost wydajności, mogę to zoptymalizować, gdy będę bliżej daty premiery. Na razie potrzebuję możliwości dynamicznego modyfikowania zawartości zarówno edytora, jak i gry. Po zablokowaniu zawartości mogę to zoptymalizować, wracając do ContentPipeline.
Ponieważ wybrałeś tę trasę, muszę cię ostrzec, że tak naprawdę nie jest tak prosta, jak skorzystanie Texture2D.FromStreamz dwóch powodów:
Problem nr 1 - Brak wstępnie pomnożonej obsługi alfa
XNA4 domyślnie obsługuje teraz tekstury z kolorami we wstępnie zmultiplikowanym formacie alfa. Gdy ładujesz teksturę przez potok zawartości, przetwarzanie jest wykonywane automatycznie. Niestety Texture2D.FromStreamnie robi tego samego, więc wszelkie tekstury wymagające pewnego stopnia przejrzystości zostaną załadowane i renderowane niepoprawnie. Poniżej znajduje się zrzut ekranu ilustrujący problem:

Aby więc uzyskać prawidłowe wyniki, musisz sam wykonać przetwarzanie. Metoda, którą pokażę, wykorzystuje procesor graficzny do przetworzenia, więc jest dość szybka. Został oparty na tym świetnym artykule . Oczywiście możesz również poinstruować, SpriteBatchaby renderować w starym trybie NonPremultiplyAlpha, ale tak naprawdę nie polecam tego robić.
Problem nr 2 - Nieobsługiwane formaty
Potok treści obsługuje więcej formatów niż Texture2D.FromStream. W szczególności Texture2D.FromStreamobsługuje tylko png, jpg i gif. Z drugiej strony, potok treści obsługuje bmp, dds, dib, hdr, jpg, pfm, png, ppm i tga. Jeśli spróbujesz załadować obsługiwany format Texture2D.FromStream, otrzymasz InvalidOperationExceptionniewiele dodatkowych informacji.
Naprawdę potrzebowałem wsparcia bmp w moim silniku, więc w tym konkretnym przypadku znalazłem obejście, które wydaje się działać dobrze. Nie znam jednak żadnego z innych formatów. Złap z moją metodą polega na tym, że musisz dodać odwołanie do System.Drawingzestawu do swojego projektu, ponieważ korzysta on z GDI, Image.FromStreamktóre obsługują więcej formatów niż Texture2D.FromStream.
Jeśli nie zależy ci na obsłudze bmp, możesz łatwo upuścić tę część mojego rozwiązania i po prostu wykonać wstępnie pomnożone przetwarzanie alfa.
Rozwiązanie - prosta wersja (wolniejsza)
Po pierwsze, oto najprostsze rozwiązanie, jeśli nie zależy ci na obsłudze bmps. W tym przykładzie etap przetwarzania odbywa się całkowicie na CPU. Jest nieco wolniejszy niż alternatywa, którą pokażę poniżej (przeprowadziłem testy porównawcze obu rozwiązań), ale łatwiej zrozumieć:
public static Texture2D FromStream(GraphicsDevice graphicsDevice, Stream stream)
{
Texture2D texture = Texture2D.FromStream(graphicsDevice, stream);
Color[] data = new Color[texture.Width * texture.Height];
texture.GetData(data);
for (int i = 0; i != data.Length; ++i)
data[i] = Color.FromNonPremultiplied(data[i].ToVector4());
texture.SetData(data);
return texture;
}
Jeśli zależy Ci na bmps, musisz najpierw załadować obraz za pomocą GDI, a następnie przekonwertować go do formatu PNG przed przekazaniem go do Texture2D.FromStream. Oto kod, który to robi:
// Load image using GDI because Texture2D.FromStream doesn't support BMP
using (Image image = Image.FromStream(stream))
{
// Now create a MemoryStream which will be passed to Texture2D after converting to PNG internally
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ms.Seek(0, SeekOrigin.Begin);
texture = Texture2D.FromStream(_graphicsDevice, ms);
}
}
Rozwiązanie - wersja złożona (szybsza)
Wreszcie podejście, które stosuję w moich projektach, polega na wykorzystaniu procesora graficznego do przetwarzania zamiast tego. W tej metodzie musisz utworzyć cel renderowania, poprawnie ustawić niektóre stany mieszania i narysować obraz dwukrotnie za pomocą SpriteBatch. Na koniec przeglądam cały RenderTarget2D i klonuję zawartość do osobnego obiektu Texture2D, ponieważ RenderTarget2D jest niestabilny i nie przetrwa takich rzeczy, jak zmiana rozmiaru bufora, więc bezpieczniej jest wykonać kopię.
Zabawne jest to, że pomimo tego wszystkiego, w moich testach to podejście działało około 3 razy szybciej niż procesor. Jest to więc zdecydowanie szybsze niż przesuwanie każdego piksela i samodzielne obliczanie koloru. Kod jest nieco długi, więc umieściłem go w koszu z kodami:
http://pastie.org/3651642
Po prostu dodaj tę klasę do swojego projektu i użyj jej tak prosto, jak:
TextureLoader textureLoader = new TextureLoader(GraphicsDevice);
Texture2D texture = textureLoader.FromFile("Content/texture.png");
Uwaga: Musisz utworzyć tylko jedną TextureLoaderinstancję dla całej gry. Używam również poprawki BMP, ale możesz ją usunąć, jeśli nie potrzebujesz i zyskać sporo wydajności, lub po prostu zostaw needsBmpparametr jako fałszywy.