blob: fad3f94339b72386fae5d4baf56ab986cb978efd [file] [log] [blame]
//
// Copyright (c) 2002-2010 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.
//
// geometry/dx9.h: Direct3D 9-based implementation of BufferBackEnd, TranslatedVertexBuffer and TranslatedIndexBuffer.
#include "libGLESv2/geometry/dx9.h"
#include <cstddef>
#define GL_APICALL
#include <GLES2/gl2.h>
#include "common/debug.h"
#include "libGLESv2/Context.h"
#include "libGLESv2/geometry/vertexconversion.h"
#include "libGLESv2/geometry/IndexDataManager.h"
namespace
{
template <class InputType, template <std::size_t IncomingWidth> class WidenRule, class ElementConverter, class DefaultValueRule = gl::SimpleDefaultValues<InputType> >
class FormatConverterFactory
{
private:
template <std::size_t IncomingWidth>
static gl::FormatConverter getFormatConverterForSize()
{
gl::FormatConverter formatConverter;
formatConverter.identity = gl::VertexDataConverter<InputType, WidenRule<IncomingWidth>, ElementConverter, DefaultValueRule>::identity;
formatConverter.outputVertexSize = gl::VertexDataConverter<InputType, WidenRule<IncomingWidth>, ElementConverter, DefaultValueRule>::finalSize;
formatConverter.convertIndexed = gl::VertexDataConverter<InputType, WidenRule<IncomingWidth>, ElementConverter, DefaultValueRule>::convertIndexed;
formatConverter.convertArray = gl::VertexDataConverter<InputType, WidenRule<IncomingWidth>, ElementConverter, DefaultValueRule>::convertArray;
return formatConverter;
}
public:
static gl::FormatConverter getFormatConverter(std::size_t size)
{
switch (size)
{
case 1: return getFormatConverterForSize<1>();
case 2: return getFormatConverterForSize<2>();
case 3: return getFormatConverterForSize<3>();
case 4: return getFormatConverterForSize<4>();
default: UNREACHABLE(); return getFormatConverterForSize<1>();
}
}
};
}
namespace gl
{
Dx9BackEnd::Dx9BackEnd(IDirect3DDevice9 *d3ddevice)
: mDevice(d3ddevice)
{
mDevice->AddRef();
}
Dx9BackEnd::~Dx9BackEnd()
{
mDevice->Release();
}
TranslatedVertexBuffer *Dx9BackEnd::createVertexBuffer(std::size_t size)
{
return new Dx9VertexBuffer(mDevice, size);
}
TranslatedVertexBuffer *Dx9BackEnd::createVertexBufferForStrideZero(std::size_t size)
{
return new Dx9VertexBufferZeroStrideWorkaround(mDevice, size);
}
TranslatedIndexBuffer *Dx9BackEnd::createIndexBuffer(std::size_t size)
{
return new Dx9IndexBuffer(mDevice, size);
}
// Mapping from OpenGL-ES vertex attrib type to D3D decl type:
//
// BYTE Translate to SHORT, expand to x2,x4 as needed.
// BYTE-norm Translate to FLOAT since it can't be exactly represented as SHORT-norm.
// UNSIGNED_BYTE x4 only. x1,x2,x3=>x4
// UNSIGNED_BYTE-norm x4 only, x1,x2,x3=>x4
// SHORT x2,x4 supported. x1=>x2, x3=>x4
// SHORT-norm x2,x4 supported. x1=>x2, x3=>x4
// UNSIGNED_SHORT unsupported, translate to float
// UNSIGNED_SHORT-norm x2,x4 supported. x1=>x2, x3=>x4
// FIXED (not in WebGL) Translate to float.
// FLOAT Fully supported.
FormatConverter Dx9BackEnd::getFormatConverter(GLenum type, std::size_t size, bool normalize)
{
// FIXME: This should be rewritten to use C99 exact-sized types.
switch (type)
{
case GL_BYTE:
if (normalize)
{
return FormatConverterFactory<char, NoWiden, Normalize<char> >::getFormatConverter(size);
}
else
{
return FormatConverterFactory<char, WidenToEven, Cast<char, short> >::getFormatConverter(size);
}
case GL_UNSIGNED_BYTE:
if (normalize)
{
return FormatConverterFactory<unsigned char, WidenToFour, Identity<unsigned char>, NormalizedDefaultValues<unsigned char> >::getFormatConverter(size);
}
else
{
return FormatConverterFactory<unsigned char, WidenToFour, Identity<unsigned char> >::getFormatConverter(size);
}
case GL_SHORT:
if (normalize)
{
return FormatConverterFactory<short, WidenToEven, Identity<short>, NormalizedDefaultValues<short> >::getFormatConverter(size);
}
else
{
return FormatConverterFactory<short, WidenToEven, Identity<short> >::getFormatConverter(size);
}
case GL_UNSIGNED_SHORT:
if (normalize)
{
return FormatConverterFactory<unsigned short, WidenToEven, Identity<unsigned short>, NormalizedDefaultValues<unsigned short> >::getFormatConverter(size);
}
else
{
return FormatConverterFactory<unsigned short, NoWiden, Cast<unsigned short, float> >::getFormatConverter(size);
}
case GL_FIXED:
return FormatConverterFactory<int, NoWiden, FixedToFloat<int, 16> >::getFormatConverter(size);
case GL_FLOAT:
return FormatConverterFactory<float, NoWiden, Identity<float> >::getFormatConverter(size);
default: UNREACHABLE(); return FormatConverterFactory<float, NoWiden, Identity<float> >::getFormatConverter(1);
}
}
D3DDECLTYPE Dx9BackEnd::mapAttributeType(GLenum type, std::size_t size, bool normalized) const
{
static const D3DDECLTYPE byteTypes[2][4] = { { D3DDECLTYPE_SHORT2, D3DDECLTYPE_SHORT2, D3DDECLTYPE_SHORT4, D3DDECLTYPE_SHORT4 }, { D3DDECLTYPE_FLOAT1, D3DDECLTYPE_FLOAT2, D3DDECLTYPE_FLOAT3, D3DDECLTYPE_FLOAT4 } };
static const D3DDECLTYPE shortTypes[2][4] = { { D3DDECLTYPE_SHORT2, D3DDECLTYPE_SHORT2, D3DDECLTYPE_SHORT4, D3DDECLTYPE_SHORT4 }, { D3DDECLTYPE_SHORT2N, D3DDECLTYPE_SHORT2N, D3DDECLTYPE_SHORT4N, D3DDECLTYPE_SHORT4N } };
static const D3DDECLTYPE ushortTypes[2][4] = { { D3DDECLTYPE_FLOAT1, D3DDECLTYPE_FLOAT2, D3DDECLTYPE_FLOAT3, D3DDECLTYPE_FLOAT4 }, { D3DDECLTYPE_USHORT2N, D3DDECLTYPE_USHORT2N, D3DDECLTYPE_USHORT4N, D3DDECLTYPE_USHORT4N } };
static const D3DDECLTYPE floatTypes[4] = { D3DDECLTYPE_FLOAT1, D3DDECLTYPE_FLOAT2, D3DDECLTYPE_FLOAT3, D3DDECLTYPE_FLOAT4 };
switch (type)
{
case GL_BYTE: return byteTypes[normalized][size-1];
case GL_UNSIGNED_BYTE: return normalized ? D3DDECLTYPE_UBYTE4N : D3DDECLTYPE_UBYTE4;
case GL_SHORT: return shortTypes[normalized][size-1];
case GL_UNSIGNED_SHORT: return ushortTypes[normalized][size-1];
case GL_FIXED:
case GL_FLOAT:
return floatTypes[size-1];
default:
UNREACHABLE();
return D3DDECLTYPE_FLOAT1;
}
}
bool Dx9BackEnd::validateStream(GLenum type, std::size_t size, std::size_t stride, std::size_t offset) const
{
// D3D9 requires the stream offset and stride to be a multiple of DWORD.
return (stride % sizeof(DWORD) == 0 && offset % sizeof(DWORD) == 0);
}
IDirect3DVertexBuffer9 *Dx9BackEnd::getDxBuffer(TranslatedVertexBuffer *vb) const
{
return vb ? static_cast<Dx9VertexBuffer*>(vb)->getBuffer() : NULL;
}
IDirect3DIndexBuffer9 *Dx9BackEnd::getDxBuffer(TranslatedIndexBuffer *ib) const
{
return ib ? static_cast<Dx9IndexBuffer*>(ib)->getBuffer() : NULL;
}
GLenum Dx9BackEnd::setupIndicesPreDraw(const TranslatedIndexData &indexInfo)
{
mDevice->SetIndices(getDxBuffer(indexInfo.buffer));
return GL_NO_ERROR;
}
GLenum Dx9BackEnd::setupAttributesPreDraw(const TranslatedAttribute *attributes)
{
HRESULT hr;
D3DVERTEXELEMENT9 elements[MAX_VERTEX_ATTRIBS+1];
D3DVERTEXELEMENT9 *nextElement = &elements[0];
for (BYTE i = 0; i < MAX_VERTEX_ATTRIBS; i++)
{
if (attributes[i].enabled)
{
nextElement->Stream = i;
nextElement->Offset = 0;
nextElement->Type = static_cast<BYTE>(mapAttributeType(attributes[i].type, attributes[i].size, attributes[i].normalized));
nextElement->Method = D3DDECLMETHOD_DEFAULT;
nextElement->Usage = D3DDECLUSAGE_TEXCOORD;
nextElement->UsageIndex = attributes[i].programAttribute;
nextElement++;
}
}
static const D3DVERTEXELEMENT9 end = D3DDECL_END();
*nextElement = end;
IDirect3DVertexDeclaration9* vertexDeclaration;
hr = mDevice->CreateVertexDeclaration(elements, &vertexDeclaration);
mDevice->SetVertexDeclaration(vertexDeclaration);
vertexDeclaration->Release();
for (size_t i = 0; i < MAX_VERTEX_ATTRIBS; i++)
{
if (attributes[i].enabled)
{
mDevice->SetStreamSource(i, getDxBuffer(attributes[i].buffer), attributes[i].offset, attributes[i].stride);
}
else
{
mDevice->SetStreamSource(i, 0, 0, 0);
}
}
return GL_NO_ERROR;
}
Dx9BackEnd::Dx9VertexBuffer::Dx9VertexBuffer(IDirect3DDevice9 *device, std::size_t size)
: TranslatedVertexBuffer(size)
{
HRESULT hr = device->CreateVertexBuffer(size, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &mVertexBuffer, NULL);
if (hr != S_OK)
{
ERR("Out of memory allocating a vertex buffer of size %lu.", size);
throw std::bad_alloc();
}
}
Dx9BackEnd::Dx9VertexBuffer::Dx9VertexBuffer(IDirect3DDevice9 *device, std::size_t size, DWORD usageFlags)
: TranslatedVertexBuffer(size)
{
HRESULT hr = device->CreateVertexBuffer(size, usageFlags, 0, D3DPOOL_DEFAULT, &mVertexBuffer, NULL);
if (hr != S_OK)
{
ERR("Out of memory allocating a vertex buffer of size %lu.", size);
throw std::bad_alloc();
}
}
Dx9BackEnd::Dx9VertexBuffer::~Dx9VertexBuffer()
{
mVertexBuffer->Release();
}
IDirect3DVertexBuffer9 *Dx9BackEnd::Dx9VertexBuffer::getBuffer() const
{
return mVertexBuffer;
}
void *Dx9BackEnd::Dx9VertexBuffer::map()
{
void *mapPtr;
mVertexBuffer->Lock(0, 0, &mapPtr, 0);
return mapPtr;
}
void Dx9BackEnd::Dx9VertexBuffer::unmap()
{
mVertexBuffer->Unlock();
}
void Dx9BackEnd::Dx9VertexBuffer::recycle()
{
void *dummy;
mVertexBuffer->Lock(0, 1, &dummy, D3DLOCK_DISCARD);
mVertexBuffer->Unlock();
}
void *Dx9BackEnd::Dx9VertexBuffer::streamingMap(std::size_t offset, std::size_t size)
{
void *mapPtr;
mVertexBuffer->Lock(offset, size, &mapPtr, D3DLOCK_NOOVERWRITE);
return mapPtr;
}
// Normally VBs are created with D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, but some hardware & drivers won't render
// if any stride-zero streams are in D3DUSAGE_DYNAMIC VBs, so this provides a way to create such VBs with only D3DUSAGE_WRITEONLY set.
// D3DLOCK_DISCARD and D3DLOCK_NOOVERWRITE are only available on D3DUSAGE_DYNAMIC VBs, so we override methods to avoid using these flags.
Dx9BackEnd::Dx9VertexBufferZeroStrideWorkaround::Dx9VertexBufferZeroStrideWorkaround(IDirect3DDevice9 *device, std::size_t size)
: Dx9VertexBuffer(device, size, D3DUSAGE_WRITEONLY)
{
}
void Dx9BackEnd::Dx9VertexBufferZeroStrideWorkaround::recycle()
{
}
void *Dx9BackEnd::Dx9VertexBufferZeroStrideWorkaround::streamingMap(std::size_t offset, std::size_t size)
{
void *mapPtr;
getBuffer()->Lock(offset, size, &mapPtr, 0);
return mapPtr;
}
Dx9BackEnd::Dx9IndexBuffer::Dx9IndexBuffer(IDirect3DDevice9 *device, std::size_t size)
: TranslatedIndexBuffer(size)
{
HRESULT hr = device->CreateIndexBuffer(size, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &mIndexBuffer, NULL);
if (hr != S_OK)
{
ERR("Out of memory allocating an index buffer of size %lu.", size);
throw std::bad_alloc();
}
}
Dx9BackEnd::Dx9IndexBuffer::~Dx9IndexBuffer()
{
mIndexBuffer->Release();
}
IDirect3DIndexBuffer9*Dx9BackEnd::Dx9IndexBuffer::getBuffer() const
{
return mIndexBuffer;
}
void *Dx9BackEnd::Dx9IndexBuffer::map()
{
void *mapPtr;
mIndexBuffer->Lock(0, 0, &mapPtr, 0);
return mapPtr;
}
void Dx9BackEnd::Dx9IndexBuffer::unmap()
{
mIndexBuffer->Unlock();
}
void Dx9BackEnd::Dx9IndexBuffer::recycle()
{
void *dummy;
mIndexBuffer->Lock(0, 1, &dummy, D3DLOCK_DISCARD);
mIndexBuffer->Unlock();
}
void *Dx9BackEnd::Dx9IndexBuffer::streamingMap(std::size_t offset, std::size_t size)
{
void *mapPtr;
mIndexBuffer->Lock(offset, size, &mapPtr, D3DLOCK_NOOVERWRITE);
return mapPtr;
}
}