Commit 362c0a79 by Geoff Lang

Implement EGL image classes for the D3D renderers.

BUG=angleproject:970 Change-Id: I24d393fcd75dd4ee510785e8475b093868701c77 Reviewed-on: https://chromium-review.googlesource.com/295152Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Tryjob-Request: Geoff Lang <geofflang@chromium.org> Tested-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 78d35692
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "libANGLE/Display.h" #include "libANGLE/Display.h"
#include "libANGLE/Surface.h" #include "libANGLE/Surface.h"
#include "libANGLE/histogram_macros.h" #include "libANGLE/histogram_macros.h"
#include "libANGLE/renderer/d3d/EGLImageD3D.h"
#include "libANGLE/renderer/d3d/RendererD3D.h" #include "libANGLE/renderer/d3d/RendererD3D.h"
#include "libANGLE/renderer/d3d/SurfaceD3D.h" #include "libANGLE/renderer/d3d/SurfaceD3D.h"
#include "libANGLE/renderer/d3d/SwapChainD3D.h" #include "libANGLE/renderer/d3d/SwapChainD3D.h"
...@@ -203,8 +204,7 @@ ImageImpl *DisplayD3D::createImage(EGLenum target, ...@@ -203,8 +204,7 @@ ImageImpl *DisplayD3D::createImage(EGLenum target,
egl::ImageSibling *buffer, egl::ImageSibling *buffer,
const egl::AttributeMap &attribs) const egl::AttributeMap &attribs)
{ {
UNIMPLEMENTED(); return new EGLImageD3D(mRenderer, target, buffer, attribs);
return nullptr;
} }
egl::Error DisplayD3D::getDevice(DeviceImpl **device) egl::Error DisplayD3D::getDevice(DeviceImpl **device)
......
//
// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// EGLImageD3D.cpp: Implements the rx::EGLImageD3D class, the D3D implementation of EGL images
#include "libANGLE/renderer/d3d/EGLImageD3D.h"
#include "common/debug.h"
#include "common/utilities.h"
#include "libANGLE/AttributeMap.h"
#include "libANGLE/Texture.h"
#include "libANGLE/renderer/d3d/RenderbufferD3D.h"
#include "libANGLE/renderer/d3d/RendererD3D.h"
#include "libANGLE/renderer/d3d/RenderTargetD3D.h"
#include "libANGLE/renderer/d3d/TextureD3D.h"
#include "libANGLE/renderer/d3d/TextureStorage.h"
#include <EGL/eglext.h>
namespace rx
{
static gl::ImageIndex GetImageIndex(GLenum target, size_t mip, size_t layer)
{
if (target == GL_TEXTURE_3D)
{
return gl::ImageIndex::Make3D(static_cast<GLint>(mip), static_cast<GLint>(layer));
}
else
{
ASSERT(layer == 0);
return gl::ImageIndex::MakeGeneric(target, static_cast<GLint>(mip));
}
}
EGLImageD3D::EGLImageD3D(RendererD3D *renderer,
EGLenum target,
egl::ImageSibling *buffer,
const egl::AttributeMap &attribs)
: mRenderer(renderer), mBuffer(buffer), mAttachmentBuffer(nullptr), mRenderTarget(nullptr)
{
ASSERT(renderer != nullptr);
ASSERT(buffer != nullptr);
if (egl::IsTextureTarget(target))
{
mAttachmentBuffer = GetImplAs<TextureD3D>(GetAs<gl::Texture>(buffer));
mAttachmentTarget = gl::FramebufferAttachment::Target(
GL_NONE, GetImageIndex(egl_gl::EGLImageTargetToGLTextureTarget(target),
attribs.get(EGL_GL_TEXTURE_LEVEL_KHR, 0),
attribs.get(EGL_GL_TEXTURE_ZOFFSET_KHR, 0)));
}
else if (egl::IsRenderbufferTarget(target))
{
mAttachmentBuffer = GetImplAs<RenderbufferD3D>(GetAs<gl::Renderbuffer>(buffer));
mAttachmentTarget =
gl::FramebufferAttachment::Target(GL_NONE, gl::ImageIndex::MakeInvalid());
}
else
{
UNREACHABLE();
}
}
EGLImageD3D::~EGLImageD3D()
{
SafeDelete(mRenderTarget);
}
egl::Error EGLImageD3D::initialize()
{
return egl::Error(EGL_SUCCESS);
}
gl::Error EGLImageD3D::orphan(egl::ImageSibling *sibling)
{
if (sibling == mBuffer)
{
gl::Error error = copyToLocalRendertarget();
if (error.isError())
{
return error;
}
}
return gl::Error(GL_NO_ERROR);
}
gl::Error EGLImageD3D::getRenderTarget(RenderTargetD3D **outRT) const
{
if (mAttachmentBuffer)
{
FramebufferAttachmentRenderTarget *rt = nullptr;
gl::Error error = mAttachmentBuffer->getAttachmentRenderTarget(mAttachmentTarget, &rt);
if (error.isError())
{
return error;
}
*outRT = static_cast<RenderTargetD3D *>(rt);
return gl::Error(GL_NO_ERROR);
}
else
{
ASSERT(mRenderTarget);
*outRT = mRenderTarget;
return gl::Error(GL_NO_ERROR);
}
}
gl::Error EGLImageD3D::copyToLocalRendertarget()
{
ASSERT(mBuffer != nullptr);
ASSERT(mAttachmentBuffer != nullptr);
ASSERT(mRenderTarget == nullptr);
RenderTargetD3D *curRenderTarget = nullptr;
gl::Error error = getRenderTarget(&curRenderTarget);
if (error.isError())
{
return error;
}
// Clear the source image buffers
mBuffer = nullptr;
mAttachmentBuffer = nullptr;
return mRenderer->createRenderTargetCopy(curRenderTarget, &mRenderTarget);
}
}
//
// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// EGLImageD3D.h: Defines the rx::EGLImageD3D class, the D3D implementation of EGL images
#ifndef LIBANGLE_RENDERER_D3D_EGLIMAGED3D_H_
#define LIBANGLE_RENDERER_D3D_EGLIMAGED3D_H_
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/renderer/ImageImpl.h"
namespace egl
{
class AttributeMap;
}
namespace rx
{
class TextureD3D;
class RenderbufferD3D;
class RendererD3D;
class RenderTargetD3D;
class EGLImageD3D final : public ImageImpl
{
public:
EGLImageD3D(RendererD3D *renderer,
EGLenum target,
egl::ImageSibling *buffer,
const egl::AttributeMap &attribs);
~EGLImageD3D() override;
egl::Error initialize() override;
gl::Error orphan(egl::ImageSibling *sibling) override;
gl::Error getRenderTarget(RenderTargetD3D **outRT) const;
private:
gl::Error copyToLocalRendertarget();
RendererD3D *mRenderer;
egl::ImageSibling *mBuffer;
gl::FramebufferAttachment::Target mAttachmentTarget;
FramebufferAttachmentObjectImpl *mAttachmentBuffer;
RenderTargetD3D *mRenderTarget;
};
}
#endif // LIBANGLE_RENDERER_D3D_EGLIMAGED3D_H_
...@@ -64,6 +64,9 @@ class ImageD3D : angle::NonCopyable ...@@ -64,6 +64,9 @@ class ImageD3D : angle::NonCopyable
const gl::ImageIndex &sourceIndex, TextureStorage *source) = 0; const gl::ImageIndex &sourceIndex, TextureStorage *source) = 0;
gl::Error copy(const gl::Offset &destOffset, const gl::Rectangle &sourceArea, const gl::Framebuffer *source); gl::Error copy(const gl::Offset &destOffset, const gl::Rectangle &sourceArea, const gl::Framebuffer *source);
virtual gl::Error copy(const gl::Offset &destOffset,
const gl::Rectangle &sourceArea,
RenderTargetD3D *source) = 0;
protected: protected:
GLsizei mWidth; GLsizei mWidth;
...@@ -74,9 +77,6 @@ class ImageD3D : angle::NonCopyable ...@@ -74,9 +77,6 @@ class ImageD3D : angle::NonCopyable
GLenum mTarget; GLenum mTarget;
bool mDirty; bool mDirty;
private:
virtual gl::Error copy(const gl::Offset &destOffset, const gl::Rectangle &sourceArea, RenderTargetD3D *source) = 0;
}; };
} }
......
...@@ -9,19 +9,22 @@ ...@@ -9,19 +9,22 @@
#include "libANGLE/renderer/d3d/RenderbufferD3D.h" #include "libANGLE/renderer/d3d/RenderbufferD3D.h"
#include "libANGLE/Image.h"
#include "libANGLE/renderer/d3d/EGLImageD3D.h"
#include "libANGLE/renderer/d3d/RendererD3D.h" #include "libANGLE/renderer/d3d/RendererD3D.h"
#include "libANGLE/renderer/d3d/RenderTargetD3D.h" #include "libANGLE/renderer/d3d/RenderTargetD3D.h"
namespace rx namespace rx
{ {
RenderbufferD3D::RenderbufferD3D(RendererD3D *renderer) : mRenderer(renderer) RenderbufferD3D::RenderbufferD3D(RendererD3D *renderer)
: mRenderer(renderer), mRenderTarget(nullptr), mImage(nullptr)
{ {
mRenderTarget = NULL;
} }
RenderbufferD3D::~RenderbufferD3D() RenderbufferD3D::~RenderbufferD3D()
{ {
SafeDelete(mRenderTarget); SafeDelete(mRenderTarget);
mImage = nullptr;
} }
gl::Error RenderbufferD3D::setStorage(GLenum internalformat, size_t width, size_t height) gl::Error RenderbufferD3D::setStorage(GLenum internalformat, size_t width, size_t height)
...@@ -62,6 +65,7 @@ gl::Error RenderbufferD3D::setStorageMultisample(size_t samples, GLenum internal ...@@ -62,6 +65,7 @@ gl::Error RenderbufferD3D::setStorageMultisample(size_t samples, GLenum internal
} }
SafeDelete(mRenderTarget); SafeDelete(mRenderTarget);
mImage = nullptr;
mRenderTarget = newRT; mRenderTarget = newRT;
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
...@@ -69,20 +73,29 @@ gl::Error RenderbufferD3D::setStorageMultisample(size_t samples, GLenum internal ...@@ -69,20 +73,29 @@ gl::Error RenderbufferD3D::setStorageMultisample(size_t samples, GLenum internal
gl::Error RenderbufferD3D::setStorageEGLImageTarget(egl::Image *image) gl::Error RenderbufferD3D::setStorageEGLImageTarget(egl::Image *image)
{ {
UNIMPLEMENTED(); mImage = GetImplAs<EGLImageD3D>(image);
SafeDelete(mRenderTarget);
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
RenderTargetD3D *RenderbufferD3D::getRenderTarget() gl::Error RenderbufferD3D::getRenderTarget(RenderTargetD3D **outRenderTarget)
{ {
return mRenderTarget; if (mImage)
{
return mImage->getRenderTarget(outRenderTarget);
}
else
{
*outRenderTarget = mRenderTarget;
return gl::Error(GL_NO_ERROR);
}
} }
gl::Error RenderbufferD3D::getAttachmentRenderTarget(const gl::FramebufferAttachment::Target &target, gl::Error RenderbufferD3D::getAttachmentRenderTarget(const gl::FramebufferAttachment::Target &target,
FramebufferAttachmentRenderTarget **rtOut) FramebufferAttachmentRenderTarget **rtOut)
{ {
*rtOut = mRenderTarget; return getRenderTarget(reinterpret_cast<RenderTargetD3D **>(rtOut));
return gl::Error(GL_NO_ERROR);
} }
} }
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
namespace rx namespace rx
{ {
class EGLImageD3D;
class RendererD3D; class RendererD3D;
class RenderTargetD3D; class RenderTargetD3D;
class SwapChainD3D; class SwapChainD3D;
...@@ -33,13 +34,14 @@ class RenderbufferD3D : public RenderbufferImpl ...@@ -33,13 +34,14 @@ class RenderbufferD3D : public RenderbufferImpl
size_t height) override; size_t height) override;
gl::Error setStorageEGLImageTarget(egl::Image *image) override; gl::Error setStorageEGLImageTarget(egl::Image *image) override;
RenderTargetD3D *getRenderTarget(); gl::Error getRenderTarget(RenderTargetD3D **outRenderTarget);
gl::Error getAttachmentRenderTarget(const gl::FramebufferAttachment::Target &target, gl::Error getAttachmentRenderTarget(const gl::FramebufferAttachment::Target &target,
FramebufferAttachmentRenderTarget **rtOut) override; FramebufferAttachmentRenderTarget **rtOut) override;
private: private:
RendererD3D *mRenderer; RendererD3D *mRenderer;
RenderTargetD3D *mRenderTarget; RenderTargetD3D *mRenderTarget;
EGLImageD3D *mImage;
}; };
} }
......
...@@ -38,6 +38,7 @@ class DebugAnnotator; ...@@ -38,6 +38,7 @@ class DebugAnnotator;
namespace rx namespace rx
{ {
struct D3DUniform; struct D3DUniform;
class EGLImageD3D;
class ImageD3D; class ImageD3D;
class IndexBuffer; class IndexBuffer;
class ProgramD3D; class ProgramD3D;
...@@ -189,6 +190,7 @@ class RendererD3D : public Renderer, public BufferFactoryD3D ...@@ -189,6 +190,7 @@ class RendererD3D : public Renderer, public BufferFactoryD3D
// RenderTarget creation // RenderTarget creation
virtual gl::Error createRenderTarget(int width, int height, GLenum format, GLsizei samples, RenderTargetD3D **outRT) = 0; virtual gl::Error createRenderTarget(int width, int height, GLenum format, GLsizei samples, RenderTargetD3D **outRT) = 0;
virtual gl::Error createRenderTargetCopy(RenderTargetD3D *source, RenderTargetD3D **outRT) = 0;
// Shader operations // Shader operations
virtual gl::Error loadExecutable(const void *function, size_t length, ShaderType type, virtual gl::Error loadExecutable(const void *function, size_t length, ShaderType type,
...@@ -205,6 +207,7 @@ class RendererD3D : public Renderer, public BufferFactoryD3D ...@@ -205,6 +207,7 @@ class RendererD3D : public Renderer, public BufferFactoryD3D
virtual gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) = 0; virtual gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) = 0;
virtual gl::Error generateMipmapsUsingD3D(TextureStorage *storage, const gl::SamplerState &samplerState) = 0; virtual gl::Error generateMipmapsUsingD3D(TextureStorage *storage, const gl::SamplerState &samplerState) = 0;
virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain) = 0; virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain) = 0;
virtual TextureStorage *createTextureStorageEGLImage(EGLImageD3D *eglImage) = 0;
virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly) = 0; virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly) = 0;
virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly) = 0; virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly) = 0;
virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels) = 0; virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels) = 0;
......
...@@ -13,11 +13,13 @@ ...@@ -13,11 +13,13 @@
#include "libANGLE/Buffer.h" #include "libANGLE/Buffer.h"
#include "libANGLE/Config.h" #include "libANGLE/Config.h"
#include "libANGLE/Framebuffer.h" #include "libANGLE/Framebuffer.h"
#include "libANGLE/Image.h"
#include "libANGLE/Surface.h" #include "libANGLE/Surface.h"
#include "libANGLE/Texture.h" #include "libANGLE/Texture.h"
#include "libANGLE/formatutils.h" #include "libANGLE/formatutils.h"
#include "libANGLE/renderer/BufferImpl.h" #include "libANGLE/renderer/BufferImpl.h"
#include "libANGLE/renderer/d3d/BufferD3D.h" #include "libANGLE/renderer/d3d/BufferD3D.h"
#include "libANGLE/renderer/d3d/EGLImageD3D.h"
#include "libANGLE/renderer/d3d/ImageD3D.h" #include "libANGLE/renderer/d3d/ImageD3D.h"
#include "libANGLE/renderer/d3d/RendererD3D.h" #include "libANGLE/renderer/d3d/RendererD3D.h"
#include "libANGLE/renderer/d3d/RenderTargetD3D.h" #include "libANGLE/renderer/d3d/RenderTargetD3D.h"
...@@ -626,6 +628,7 @@ gl::Error TextureD3D::getAttachmentRenderTarget(const gl::FramebufferAttachment: ...@@ -626,6 +628,7 @@ gl::Error TextureD3D::getAttachmentRenderTarget(const gl::FramebufferAttachment:
TextureD3D_2D::TextureD3D_2D(RendererD3D *renderer) TextureD3D_2D::TextureD3D_2D(RendererD3D *renderer)
: TextureD3D(renderer) : TextureD3D(renderer)
{ {
mEGLImageTarget = false;
for (int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i) for (int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i)
{ {
mImageArray[i] = renderer->createImage(); mImageArray[i] = renderer->createImage();
...@@ -984,6 +987,7 @@ void TextureD3D_2D::bindTexImage(egl::Surface *surface) ...@@ -984,6 +987,7 @@ void TextureD3D_2D::bindTexImage(egl::Surface *surface)
ASSERT(surfaceD3D); ASSERT(surfaceD3D);
mTexStorage = mRenderer->createTextureStorage2D(surfaceD3D->getSwapChain()); mTexStorage = mRenderer->createTextureStorage2D(surfaceD3D->getSwapChain());
mEGLImageTarget = false;
mDirtyImages = true; mDirtyImages = true;
} }
...@@ -997,13 +1001,31 @@ void TextureD3D_2D::releaseTexImage() ...@@ -997,13 +1001,31 @@ void TextureD3D_2D::releaseTexImage()
for (int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) for (int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
{ {
mImageArray[i]->redefine(GL_TEXTURE_2D, GL_NONE, gl::Extents(0, 0, 0), true); redefineImage(i, GL_NONE, gl::Extents(0, 0, 1), true);
} }
} }
gl::Error TextureD3D_2D::setEGLImageTarget(GLenum target, egl::Image *image) gl::Error TextureD3D_2D::setEGLImageTarget(GLenum target, egl::Image *image)
{ {
UNIMPLEMENTED(); EGLImageD3D *eglImaged3d = GetImplAs<EGLImageD3D>(image);
// Set the properties of the base mip level from the EGL image
GLenum internalformat = image->getInternalFormat();
gl::Extents size(static_cast<int>(image->getWidth()), static_cast<int>(image->getHeight()), 1);
redefineImage(0, internalformat, size, true);
// Clear all other images.
for (size_t level = 1; level < ArraySize(mImageArray); level++)
{
redefineImage(level, GL_NONE, gl::Extents(0, 0, 1), true);
}
SafeDelete(mTexStorage);
mImageArray[0]->markClean();
mTexStorage = mRenderer->createTextureStorageEGLImage(eglImaged3d);
mEGLImageTarget = true;
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
...@@ -1245,6 +1267,22 @@ void TextureD3D_2D::redefineImage(size_t level, ...@@ -1245,6 +1267,22 @@ void TextureD3D_2D::redefineImage(size_t level,
{ {
const size_t storageLevels = mTexStorage->getLevelCount(); const size_t storageLevels = mTexStorage->getLevelCount();
// If the storage was from an EGL image, copy it back into local images to preserve it
// while orphaning
if (level != 0 && mEGLImageTarget)
{
gl::Offset offset(0, 0, 0);
gl::Rectangle sourceArea(0, 0, mImageArray[0]->getWidth(), mImageArray[0]->getHeight());
RenderTargetD3D *storageRendertarget = nullptr;
gl::Error error =
mTexStorage->getRenderTarget(gl::ImageIndex::Make2D(0), &storageRendertarget);
if (!error.isError())
{
mImageArray[0]->copy(offset, sourceArea, storageRendertarget);
}
}
if ((level >= storageLevels && storageLevels != 0) || if ((level >= storageLevels && storageLevels != 0) ||
size.width != storageWidth || size.width != storageWidth ||
size.height != storageHeight || size.height != storageHeight ||
...@@ -1259,6 +1297,9 @@ void TextureD3D_2D::redefineImage(size_t level, ...@@ -1259,6 +1297,9 @@ void TextureD3D_2D::redefineImage(size_t level,
mDirtyImages = true; mDirtyImages = true;
} }
} }
// Can't be an EGL image target after being redefined
mEGLImageTarget = false;
} }
gl::ImageIndexIterator TextureD3D_2D::imageIterator() const gl::ImageIndexIterator TextureD3D_2D::imageIterator() const
......
...@@ -20,8 +20,7 @@ class Framebuffer; ...@@ -20,8 +20,7 @@ class Framebuffer;
namespace rx namespace rx
{ {
class EGLImageD3D;
class ImageD3D;
class ImageD3D; class ImageD3D;
class RendererD3D; class RendererD3D;
class RenderTargetD3D; class RenderTargetD3D;
...@@ -178,6 +177,7 @@ class TextureD3D_2D : public TextureD3D ...@@ -178,6 +177,7 @@ class TextureD3D_2D : public TextureD3D
const gl::Extents &size, const gl::Extents &size,
bool forceRelease); bool forceRelease);
bool mEGLImageTarget;
ImageD3D *mImageArray[gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS]; ImageD3D *mImageArray[gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS];
}; };
......
...@@ -787,6 +787,12 @@ void Renderer11::generateDisplayExtensions(egl::DisplayExtensions *outExtensions ...@@ -787,6 +787,12 @@ void Renderer11::generateDisplayExtensions(egl::DisplayExtensions *outExtensions
outExtensions->createContext = true; outExtensions->createContext = true;
outExtensions->deviceQuery = true; outExtensions->deviceQuery = true;
outExtensions->image = true;
outExtensions->imageBase = true;
outExtensions->glTexture2DImage = true;
outExtensions->glTextureCubemapImage = true;
outExtensions->glRenderbufferImage = true;
} }
gl::Error Renderer11::flush() gl::Error Renderer11::flush()
...@@ -2973,6 +2979,28 @@ gl::Error Renderer11::createRenderTarget(int width, int height, GLenum format, G ...@@ -2973,6 +2979,28 @@ gl::Error Renderer11::createRenderTarget(int width, int height, GLenum format, G
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
gl::Error Renderer11::createRenderTargetCopy(RenderTargetD3D *source, RenderTargetD3D **outRT)
{
ASSERT(source != nullptr);
RenderTargetD3D *newRT = nullptr;
gl::Error error = createRenderTarget(source->getWidth(), source->getHeight(),
source->getInternalFormat(), source->getSamples(), &newRT);
if (error.isError())
{
return error;
}
RenderTarget11 *source11 = GetAs<RenderTarget11>(source);
RenderTarget11 *dest11 = GetAs<RenderTarget11>(newRT);
mDeviceContext->CopySubresourceRegion(dest11->getTexture(), dest11->getSubresourceIndex(), 0, 0,
0, source11->getTexture(),
source11->getSubresourceIndex(), nullptr);
*outRT = newRT;
return gl::Error(GL_NO_ERROR);
}
FramebufferImpl *Renderer11::createFramebuffer(const gl::Framebuffer::Data &data) FramebufferImpl *Renderer11::createFramebuffer(const gl::Framebuffer::Data &data)
{ {
return new Framebuffer11(data, this); return new Framebuffer11(data, this);
...@@ -3288,6 +3316,11 @@ TextureStorage *Renderer11::createTextureStorage2D(SwapChainD3D *swapChain) ...@@ -3288,6 +3316,11 @@ TextureStorage *Renderer11::createTextureStorage2D(SwapChainD3D *swapChain)
return new TextureStorage11_2D(this, swapChain11); return new TextureStorage11_2D(this, swapChain11);
} }
TextureStorage *Renderer11::createTextureStorageEGLImage(EGLImageD3D *eglImage)
{
return new TextureStorage11_EGLImage(this, eglImage);
}
TextureStorage *Renderer11::createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly) TextureStorage *Renderer11::createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly)
{ {
return new TextureStorage11_2D(this, internalformat, renderTarget, width, height, levels, hintLevelZeroOnly); return new TextureStorage11_2D(this, internalformat, renderTarget, width, height, levels, hintLevelZeroOnly);
......
...@@ -176,6 +176,7 @@ class Renderer11 : public RendererD3D ...@@ -176,6 +176,7 @@ class Renderer11 : public RendererD3D
// RenderTarget creation // RenderTarget creation
virtual gl::Error createRenderTarget(int width, int height, GLenum format, GLsizei samples, RenderTargetD3D **outRT); virtual gl::Error createRenderTarget(int width, int height, GLenum format, GLsizei samples, RenderTargetD3D **outRT);
gl::Error createRenderTargetCopy(RenderTargetD3D *source, RenderTargetD3D **outRT) override;
// Framebuffer creation // Framebuffer creation
FramebufferImpl *createFramebuffer(const gl::Framebuffer::Data &data) override; FramebufferImpl *createFramebuffer(const gl::Framebuffer::Data &data) override;
...@@ -200,6 +201,7 @@ class Renderer11 : public RendererD3D ...@@ -200,6 +201,7 @@ class Renderer11 : public RendererD3D
gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override; gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override;
gl::Error generateMipmapsUsingD3D(TextureStorage *storage, const gl::SamplerState &samplerState) override; gl::Error generateMipmapsUsingD3D(TextureStorage *storage, const gl::SamplerState &samplerState) override;
virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain); virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain);
TextureStorage *createTextureStorageEGLImage(EGLImageD3D *eglImage) override;
virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly); virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly);
virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly); virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly);
virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels); virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels);
......
...@@ -23,6 +23,7 @@ struct ImageIndex; ...@@ -23,6 +23,7 @@ struct ImageIndex;
namespace rx namespace rx
{ {
class EGLImageD3D;
class RenderTargetD3D; class RenderTargetD3D;
class RenderTarget11; class RenderTarget11;
class Renderer11; class Renderer11;
...@@ -93,6 +94,9 @@ class TextureStorage11 : public TextureStorage ...@@ -93,6 +94,9 @@ class TextureStorage11 : public TextureStorage
void verifySwizzleExists(GLenum swizzleRed, GLenum swizzleGreen, GLenum swizzleBlue, GLenum swizzleAlpha); void verifySwizzleExists(GLenum swizzleRed, GLenum swizzleGreen, GLenum swizzleBlue, GLenum swizzleAlpha);
// Clear all cached non-swizzle SRVs and invalidate the swizzle cache.
void clearSRVCache();
Renderer11 *mRenderer; Renderer11 *mRenderer;
int mTopLevel; int mTopLevel;
unsigned int mMipLevels; unsigned int mMipLevels;
...@@ -197,6 +201,52 @@ class TextureStorage11_2D : public TextureStorage11 ...@@ -197,6 +201,52 @@ class TextureStorage11_2D : public TextureStorage11
Image11 *mAssociatedImages[gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS]; Image11 *mAssociatedImages[gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS];
}; };
class TextureStorage11_EGLImage final : public TextureStorage11
{
public:
TextureStorage11_EGLImage(Renderer11 *renderer, EGLImageD3D *eglImage);
~TextureStorage11_EGLImage() override;
gl::Error getResource(ID3D11Resource **outResource) override;
gl::Error getSRV(const gl::SamplerState &samplerState,
ID3D11ShaderResourceView **outSRV) override;
gl::Error getMippedResource(ID3D11Resource **outResource) override;
gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT) override;
gl::Error copyToStorage(TextureStorage *destStorage) override;
void associateImage(Image11 *image, const gl::ImageIndex &index) override;
void disassociateImage(const gl::ImageIndex &index, Image11 *expectedImage) override;
bool isAssociatedImageValid(const gl::ImageIndex &index, Image11 *expectedImage) override;
gl::Error releaseAssociatedImage(const gl::ImageIndex &index, Image11 *incomingImage) override;
gl::Error useLevelZeroWorkaroundTexture(bool useLevelZeroTexture) override;
protected:
gl::Error getSwizzleTexture(ID3D11Resource **outTexture) override;
gl::Error getSwizzleRenderTarget(int mipLevel, ID3D11RenderTargetView **outRTV) override;
private:
// Check if the EGL image's render target has been updated due to orphaning and delete
// any SRVs and other resources based on the image's old render target.
gl::Error checkForUpdatedRenderTarget();
gl::Error createSRV(int baseLevel,
int mipLevels,
DXGI_FORMAT format,
ID3D11Resource *texture,
ID3D11ShaderResourceView **outSRV) const override;
gl::Error getImageRenderTarget(RenderTarget11 **outRT) const;
EGLImageD3D *mImage;
uintptr_t mCurrentRenderTarget;
// Swizzle-related variables
ID3D11Texture2D *mSwizzleTexture;
std::vector<ID3D11RenderTargetView *> mSwizzleRenderTargets;
};
class TextureStorage11_Cube : public TextureStorage11 class TextureStorage11_Cube : public TextureStorage11
{ {
public: public:
......
...@@ -1211,6 +1211,7 @@ void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, cons ...@@ -1211,6 +1211,7 @@ void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, cons
extensions->translatedShaderSource = true; extensions->translatedShaderSource = true;
extensions->fboRenderMipmap = false; extensions->fboRenderMipmap = false;
extensions->debugMarker = true; extensions->debugMarker = true;
extensions->eglImage = true;
// D3D11 Feature Level 10_0+ uses SV_IsFrontFace in HLSL to emulate gl_FrontFacing. // D3D11 Feature Level 10_0+ uses SV_IsFrontFace in HLSL to emulate gl_FrontFacing.
// D3D11 Feature Level 9_3 doesn't support SV_IsFrontFace, and has no equivalent, so can't support gl_FrontFacing. // D3D11 Feature Level 9_3 doesn't support SV_IsFrontFace, and has no equivalent, so can't support gl_FrontFacing.
......
...@@ -523,6 +523,11 @@ void Renderer9::generateDisplayExtensions(egl::DisplayExtensions *outExtensions) ...@@ -523,6 +523,11 @@ void Renderer9::generateDisplayExtensions(egl::DisplayExtensions *outExtensions)
outExtensions->postSubBuffer = true; outExtensions->postSubBuffer = true;
outExtensions->createContext = true; outExtensions->createContext = true;
outExtensions->deviceQuery = true; outExtensions->deviceQuery = true;
outExtensions->image = true;
outExtensions->imageBase = true;
outExtensions->glTexture2DImage = true;
outExtensions->glRenderbufferImage = true;
} }
void Renderer9::startScene() void Renderer9::startScene()
...@@ -2675,6 +2680,33 @@ gl::Error Renderer9::createRenderTarget(int width, int height, GLenum format, GL ...@@ -2675,6 +2680,33 @@ gl::Error Renderer9::createRenderTarget(int width, int height, GLenum format, GL
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
gl::Error Renderer9::createRenderTargetCopy(RenderTargetD3D *source, RenderTargetD3D **outRT)
{
ASSERT(source != nullptr);
RenderTargetD3D *newRT = nullptr;
gl::Error error = createRenderTarget(source->getWidth(), source->getHeight(),
source->getInternalFormat(), source->getSamples(), &newRT);
if (error.isError())
{
return error;
}
RenderTarget9 *source9 = GetAs<RenderTarget9>(source);
RenderTarget9 *dest9 = GetAs<RenderTarget9>(newRT);
HRESULT result = mDevice->StretchRect(source9->getSurface(), nullptr, dest9->getSurface(),
nullptr, D3DTEXF_NONE);
if (FAILED(result))
{
ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
return gl::Error(GL_OUT_OF_MEMORY, "Failed to copy render target, result: 0x%X.", result);
}
*outRT = newRT;
return gl::Error(GL_NO_ERROR);
}
FramebufferImpl *Renderer9::createFramebuffer(const gl::Framebuffer::Data &data) FramebufferImpl *Renderer9::createFramebuffer(const gl::Framebuffer::Data &data)
{ {
return new Framebuffer9(data, this); return new Framebuffer9(data, this);
...@@ -2906,6 +2938,11 @@ TextureStorage *Renderer9::createTextureStorage2D(SwapChainD3D *swapChain) ...@@ -2906,6 +2938,11 @@ TextureStorage *Renderer9::createTextureStorage2D(SwapChainD3D *swapChain)
return new TextureStorage9_2D(this, swapChain9); return new TextureStorage9_2D(this, swapChain9);
} }
TextureStorage *Renderer9::createTextureStorageEGLImage(EGLImageD3D *eglImage)
{
return new TextureStorage9_EGLImage(this, eglImage);
}
TextureStorage *Renderer9::createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly) TextureStorage *Renderer9::createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly)
{ {
return new TextureStorage9_2D(this, internalformat, renderTarget, width, height, levels); return new TextureStorage9_2D(this, internalformat, renderTarget, width, height, levels);
......
...@@ -163,6 +163,7 @@ class Renderer9 : public RendererD3D ...@@ -163,6 +163,7 @@ class Renderer9 : public RendererD3D
// RenderTarget creation // RenderTarget creation
virtual gl::Error createRenderTarget(int width, int height, GLenum format, GLsizei samples, RenderTargetD3D **outRT); virtual gl::Error createRenderTarget(int width, int height, GLenum format, GLsizei samples, RenderTargetD3D **outRT);
gl::Error createRenderTargetCopy(RenderTargetD3D *source, RenderTargetD3D **outRT) override;
// Framebuffer creation // Framebuffer creation
FramebufferImpl *createFramebuffer(const gl::Framebuffer::Data &data) override; FramebufferImpl *createFramebuffer(const gl::Framebuffer::Data &data) override;
...@@ -187,6 +188,7 @@ class Renderer9 : public RendererD3D ...@@ -187,6 +188,7 @@ class Renderer9 : public RendererD3D
gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override; gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override;
gl::Error generateMipmapsUsingD3D(TextureStorage *storage, const gl::SamplerState &samplerState) override; gl::Error generateMipmapsUsingD3D(TextureStorage *storage, const gl::SamplerState &samplerState) override;
virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain); virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain);
TextureStorage *createTextureStorageEGLImage(EGLImageD3D *eglImage) override;
virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly); virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly);
virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly); virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly);
virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels); virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels);
......
...@@ -9,14 +9,16 @@ ...@@ -9,14 +9,16 @@
// D3D9 texture. // D3D9 texture.
#include "libANGLE/renderer/d3d/d3d9/TextureStorage9.h" #include "libANGLE/renderer/d3d/d3d9/TextureStorage9.h"
#include "libANGLE/renderer/d3d/d3d9/Renderer9.h"
#include "libANGLE/renderer/d3d/d3d9/SwapChain9.h"
#include "libANGLE/renderer/d3d/d3d9/RenderTarget9.h"
#include "libANGLE/renderer/d3d/d3d9/renderer9_utils.h"
#include "libANGLE/renderer/d3d/d3d9/formatutils9.h"
#include "libANGLE/renderer/d3d/TextureD3D.h"
#include "libANGLE/formatutils.h" #include "libANGLE/formatutils.h"
#include "libANGLE/Texture.h" #include "libANGLE/Texture.h"
#include "libANGLE/renderer/d3d/EGLImageD3D.h"
#include "libANGLE/renderer/d3d/TextureD3D.h"
#include "libANGLE/renderer/d3d/d3d9/formatutils9.h"
#include "libANGLE/renderer/d3d/d3d9/renderer9_utils.h"
#include "libANGLE/renderer/d3d/d3d9/Renderer9.h"
#include "libANGLE/renderer/d3d/d3d9/RenderTarget9.h"
#include "libANGLE/renderer/d3d/d3d9/SwapChain9.h"
namespace rx namespace rx
{ {
...@@ -303,6 +305,131 @@ gl::Error TextureStorage9_2D::copyToStorage(TextureStorage *destStorage) ...@@ -303,6 +305,131 @@ gl::Error TextureStorage9_2D::copyToStorage(TextureStorage *destStorage)
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
TextureStorage9_EGLImage::TextureStorage9_EGLImage(Renderer9 *renderer, EGLImageD3D *image)
: TextureStorage9(renderer, D3DUSAGE_RENDERTARGET), mImage(image)
{
RenderTargetD3D *renderTargetD3D = nullptr;
mImage->getRenderTarget(&renderTargetD3D);
RenderTarget9 *renderTarget9 = GetAs<RenderTarget9>(renderTargetD3D);
mInternalFormat = renderTarget9->getInternalFormat();
mTextureFormat = renderTarget9->getD3DFormat();
mTextureWidth = renderTarget9->getWidth();
mTextureHeight = renderTarget9->getHeight();
mTopLevel = static_cast<int>(renderTarget9->getTextureLevel());
mMipLevels = mTopLevel + 1;
}
TextureStorage9_EGLImage::~TextureStorage9_EGLImage()
{
}
gl::Error TextureStorage9_EGLImage::getSurfaceLevel(GLenum target,
int level,
bool,
IDirect3DSurface9 **outSurface)
{
ASSERT(target == GL_TEXTURE_2D);
ASSERT(level == 0);
UNUSED_ASSERTION_VARIABLE(target);
UNUSED_ASSERTION_VARIABLE(level);
RenderTargetD3D *renderTargetD3D = nullptr;
gl::Error error = mImage->getRenderTarget(&renderTargetD3D);
if (error.isError())
{
return error;
}
RenderTarget9 *renderTarget9 = GetAs<RenderTarget9>(renderTargetD3D);
*outSurface = renderTarget9->getSurface();
return gl::Error(GL_NO_ERROR);
}
gl::Error TextureStorage9_EGLImage::getRenderTarget(const gl::ImageIndex &index,
RenderTargetD3D **outRT)
{
ASSERT(!index.hasLayer());
ASSERT(index.mipIndex == 0);
UNUSED_ASSERTION_VARIABLE(index);
return mImage->getRenderTarget(outRT);
}
gl::Error TextureStorage9_EGLImage::getBaseTexture(IDirect3DBaseTexture9 **outTexture)
{
RenderTargetD3D *renderTargetD3D = nullptr;
gl::Error error = mImage->getRenderTarget(&renderTargetD3D);
if (error.isError())
{
return error;
}
RenderTarget9 *renderTarget9 = GetAs<RenderTarget9>(renderTargetD3D);
*outTexture = renderTarget9->getTexture();
ASSERT(*outTexture != nullptr);
return gl::Error(GL_NO_ERROR);
}
gl::Error TextureStorage9_EGLImage::generateMipmap(const gl::ImageIndex &, const gl::ImageIndex &)
{
UNREACHABLE();
return gl::Error(GL_INVALID_OPERATION);
}
gl::Error TextureStorage9_EGLImage::copyToStorage(TextureStorage *destStorage)
{
ASSERT(destStorage);
ASSERT(getLevelCount() == 1);
TextureStorage9 *dest9 = GetAs<TextureStorage9>(destStorage);
IDirect3DBaseTexture9 *destBaseTexture9 = nullptr;
gl::Error error = dest9->getBaseTexture(&destBaseTexture9);
if (error.isError())
{
return error;
}
IDirect3DTexture9 *destTexture9 = static_cast<IDirect3DTexture9 *>(destBaseTexture9);
IDirect3DSurface9 *destSurface = nullptr;
HRESULT result = destTexture9->GetSurfaceLevel(destStorage->getTopLevel(), &destSurface);
if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY,
"Failed to get the surface from a texture, result: 0x%X.", result);
}
RenderTargetD3D *sourceRenderTarget = nullptr;
error = mImage->getRenderTarget(&sourceRenderTarget);
if (error.isError())
{
SafeRelease(destSurface);
return error;
}
RenderTarget9 *sourceRenderTarget9 = GetAs<RenderTarget9>(sourceRenderTarget);
error =
mRenderer->copyToRenderTarget(destSurface, sourceRenderTarget9->getSurface(), isManaged());
if (error.isError())
{
SafeRelease(destSurface);
return error;
}
if (destStorage->getTopLevel() != 0)
{
destTexture9->AddDirtyRect(nullptr);
}
SafeRelease(destSurface);
return gl::Error(GL_NO_ERROR);
}
TextureStorage9_Cube::TextureStorage9_Cube(Renderer9 *renderer, GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly) TextureStorage9_Cube::TextureStorage9_Cube(Renderer9 *renderer, GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly)
: TextureStorage9(renderer, GetTextureUsage(internalformat, renderTarget)) : TextureStorage9(renderer, GetTextureUsage(internalformat, renderTarget))
{ {
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
namespace rx namespace rx
{ {
class EGLImageD3D;
class Renderer9; class Renderer9;
class SwapChain9; class SwapChain9;
class RenderTargetD3D; class RenderTargetD3D;
...@@ -85,6 +86,26 @@ class TextureStorage9_2D : public TextureStorage9 ...@@ -85,6 +86,26 @@ class TextureStorage9_2D : public TextureStorage9
std::vector<RenderTarget9 *> mRenderTargets; std::vector<RenderTarget9 *> mRenderTargets;
}; };
class TextureStorage9_EGLImage final : public TextureStorage9
{
public:
TextureStorage9_EGLImage(Renderer9 *renderer, EGLImageD3D *image);
~TextureStorage9_EGLImage() override;
gl::Error getSurfaceLevel(GLenum target,
int level,
bool dirty,
IDirect3DSurface9 **outSurface) override;
gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT) override;
gl::Error getBaseTexture(IDirect3DBaseTexture9 **outTexture) override;
gl::Error generateMipmap(const gl::ImageIndex &sourceIndex,
const gl::ImageIndex &destIndex) override;
gl::Error copyToStorage(TextureStorage *destStorage) override;
private:
EGLImageD3D *mImage;
};
class TextureStorage9_Cube : public TextureStorage9 class TextureStorage9_Cube : public TextureStorage9
{ {
public: public:
......
...@@ -573,6 +573,7 @@ void GenerateCaps(IDirect3D9 *d3d9, ...@@ -573,6 +573,7 @@ void GenerateCaps(IDirect3D9 *d3d9,
extensions->discardFramebuffer = false; // It would be valid to set this to true, since glDiscardFramebufferEXT is just a hint extensions->discardFramebuffer = false; // It would be valid to set this to true, since glDiscardFramebufferEXT is just a hint
extensions->colorBufferFloat = false; extensions->colorBufferFloat = false;
extensions->debugMarker = true; extensions->debugMarker = true;
extensions->eglImage = true;
// D3D9 has no concept of separate masks and refs for front and back faces in the depth stencil // D3D9 has no concept of separate masks and refs for front and back faces in the depth stencil
// state. // state.
......
...@@ -177,6 +177,8 @@ ...@@ -177,6 +177,8 @@
'libANGLE/renderer/d3d/DisplayD3D.h', 'libANGLE/renderer/d3d/DisplayD3D.h',
'libANGLE/renderer/d3d/DynamicHLSL.cpp', 'libANGLE/renderer/d3d/DynamicHLSL.cpp',
'libANGLE/renderer/d3d/DynamicHLSL.h', 'libANGLE/renderer/d3d/DynamicHLSL.h',
'libANGLE/renderer/d3d/EGLImageD3D.cpp',
'libANGLE/renderer/d3d/EGLImageD3D.h',
'libANGLE/renderer/d3d/formatutilsD3D.cpp', 'libANGLE/renderer/d3d/formatutilsD3D.cpp',
'libANGLE/renderer/d3d/formatutilsD3D.h', 'libANGLE/renderer/d3d/formatutilsD3D.h',
'libANGLE/renderer/d3d/FramebufferD3D.cpp', 'libANGLE/renderer/d3d/FramebufferD3D.cpp',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment