/*
Copyright (C) 2010, Heikki Salo
All rights reserved.
Distributed under the BSD license:
http://www.opensource.org/licenses/bsd-license.php
*/
#include "stdafx.h"
#include "DPTexture.hpp"
#include "DPDevice.hpp"
#include "DPUtils.hpp"
DPTexture::~DPTexture()
{
release();
}
DPTexture::DPTexture(ID3D11Texture2D* tex)
{
//Used internally by DirectPython.
mapData.pData = 0;
texture = tex;
texture->AddRef();
DPBase::init(texture);
}
DPTexture::DPTexture(const char* path, python::object& sizes, UINT argFormat,
UINT argBind, UINT argUsage, UINT argCpuAccess, UINT miscFlags)
{
mapData.pData = 0;
texture = 0;
DPDevice* device = getDPDevice();
UINT width = python::extract<UINT>(sizes[0]);
UINT height = python::extract<UINT>(sizes[1]);
UINT arraySize = 1;
UINT mipMapCount = 0;
UINT argSizeLen = python::len(sizes);
if (argSizeLen >= 3) {
arraySize = python::extract<UINT>(sizes[2]);
if (argSizeLen >= 4)
mipMapCount = python::extract<UINT>(sizes[3]);
}
D3D11_USAGE usage = (D3D11_USAGE)argUsage; //D3D11_USAGE_DEFAULT;
D3D11_BIND_FLAG bind = (D3D11_BIND_FLAG)argBind; //D3D11_BIND_SHADER_RESOURCE;
UINT cpuAccess = argCpuAccess;
DXGI_FORMAT format = (argFormat == 0) ? (DXGI_FORMAT)D3DX11_DEFAULT : (DXGI_FORMAT)argFormat;
if (path == 0) {
D3D11_TEXTURE2D_DESC texDesc;
texDesc.Width = width;
texDesc.Height = height;
texDesc.MipLevels = mipMapCount;
texDesc.ArraySize = arraySize;
texDesc.Format = format;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = usage;
texDesc.BindFlags = bind;
texDesc.CPUAccessFlags = cpuAccess;
texDesc.MiscFlags = miscFlags;
HRESULT hr = device->getDevice()->CreateTexture2D(&texDesc, NULL, &texture);
if (FAILED(hr))
DPThrow(hr);
}
else {
D3DX11_IMAGE_INFO imageInfo; //XXX not used anymore.
ZeroMemory(&imageInfo, sizeof(imageInfo));
D3DX11_IMAGE_LOAD_INFO loadInfo;
loadInfo.Width = width;
loadInfo.Height = height;
loadInfo.Depth = D3DX11_DEFAULT;
loadInfo.FirstMipLevel = 0;
loadInfo.MipLevels = (mipMapCount == 0) ? D3DX11_DEFAULT : mipMapCount;
loadInfo.Usage = usage;
loadInfo.BindFlags = bind;
loadInfo.CpuAccessFlags = cpuAccess;
loadInfo.MiscFlags = miscFlags;
loadInfo.Format = format;
loadInfo.Filter = D3DX11_FILTER_LINEAR;
loadInfo.MipFilter = D3DX11_FILTER_LINEAR;
loadInfo.pSrcInfo = &imageInfo;
ID3D11Resource* resource = 0;
HRESULT hr;
Py_BEGIN_ALLOW_THREADS //Not 100% safe.
hr = D3DX11CreateTextureFromFileA(device->getDevice(), path,
&loadInfo, NULL, &resource, NULL);
Py_END_ALLOW_THREADS
if (FAILED(hr))
DPThrow(hr);
hr = resource->QueryInterface<ID3D11Texture2D>(&texture);
SafeRelease(resource);
if (FAILED(hr)) {
DPThrow("Can't get a 2D texture interface");
}
}
DPBase::init(texture);
}
void DPTexture::release()
{
SafeRelease(texture);
}
void DPTexture::resolve(DPTexture& src)
{
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
getContext()->ResolveSubresource(texture,
0, src.texture, 0, desc.Format);
}
void DPTexture::copy(DPTexture& src)
{
getContext()->CopyResource(texture, src.texture);
}
void DPTexture::copyRegion(UINT destx, UINT desty, UINT w, UINT h, UINT srcx, UINT scry,
DPTexture& src, UINT dstindex, UINT srcindex)
{
// -1 because box.left is non-inclusive.
if (!pointInside(destx + w - 1, desty + h - 1) || !src.pointInside(srcx + w - 1, scry + h - 1))
DPThrow("Area out of bounds");
D3D11_TEXTURE2D_DESC srcDesc, destDesc;
texture->GetDesc(&destDesc);
src.getTexture()->GetDesc(&srcDesc);
D3D11_BOX sourceRegion;
sourceRegion.left = srcx;
sourceRegion.top = scry;
sourceRegion.front = 0;
sourceRegion.right = srcx + w;
sourceRegion.bottom = scry + h;
sourceRegion.back = 1;
getContext()->CopySubresourceRegion(texture,
D3D11CalcSubresource(0, dstindex, destDesc.MipLevels),
destx, desty, 0, src.texture,
D3D11CalcSubresource(0, srcindex, srcDesc.MipLevels), &sourceRegion);
}
void DPTexture::filter(UINT filterValue)
{
HRESULT hr = D3DX11FilterTexture(getContext(), texture, 0, filterValue);
TestHR(hr);
}
void DPTexture::save(const char* path, UINT format)
{
HRESULT hr;
ID3D11DeviceContext* context = getContext();
Py_BEGIN_ALLOW_THREADS
hr = D3DX11SaveTextureToFileA(context, texture,
(D3DX11_IMAGE_FILE_FORMAT)format, path);
Py_END_ALLOW_THREADS
TestHR(hr);
}
python::object DPTexture::size()
{
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
return python::make_tuple(desc.Width, desc.Height);
}
UINT DPTexture::itemSize()
{
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
return GetFormatByteSize(desc.Format);
}
python::str DPTexture::getItemFormat()
{
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
std::string result;
GetFormatByteSize(desc.Format, &result);
return python::str(result.c_str());
}
python::object DPTexture::description()
{
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
static python::object TextureDesc = CreateNamedTuple("TextureDesc",
"size format bind usage cpuAccess miscFlags");
return TextureDesc(python::make_tuple(desc.Width, desc.Height,
desc.ArraySize, desc.MipLevels), (UINT)desc.Format,
desc.BindFlags, (UINT)desc.Usage, desc.CPUAccessFlags, desc.MiscFlags);
}
bool DPTexture::pointInside(UINT x, UINT y)
{
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
if (x >= desc.Width || y >= desc.Height)
return false;
return true;
}
UINT getSliceValue(const python::object& obj, UINT defval)
{
if (obj != python::object())
return python::extract<UINT>(obj);
return defval;
}
python::object DPTexture::getorsetitem(python::object& indices, python::object* values, const bool getter)
{
D3D11_TEXTURE2D_DESC texDesc;
texture->GetDesc(&texDesc);
python::slice xAxis = python::extract<python::slice>(indices[0]);
python::slice yAxis = python::extract<python::slice>(indices[1]);
const UINT xStart = getSliceValue(xAxis.start(), 0);
const UINT xEnd = getSliceValue(xAxis.stop(), texDesc.Width);
const UINT xStep = getSliceValue(xAxis.step(), 1);
const UINT yStart = getSliceValue(yAxis.start(), 0);
const UINT yEnd = getSliceValue(yAxis.stop(), texDesc.Height);
const UINT yStep = getSliceValue(yAxis.step(), 1);
if (xStep == 0 || yStep == 0)
DPThrow("Step can't be 0");
//-1 for non inclusive end-bounds.
if (!pointInside(xStart, yStart) || !pointInside(xEnd - 1, yEnd - 1))
DPThrow("Slice outside the texture");
if (yEnd <= yStart || xEnd <= xStart)
DPThrow("Negative or 0 slice size");
const UINT elementByteSize = GetFormatByteSize(texDesc.Format);
const UINT xSpan = xEnd - xStart;
const UINT ySpan = yEnd - yStart;
python::object result;
const UINT bytesTotal = (xSpan / xStep) * (ySpan / yStep) * elementByteSize;
if (getter) {
PyObject* tmp = 0;
#if PY_VERSION_HEX >= 0x02060000
tmp = PyByteArray_FromStringAndSize(0, bytesTotal);
#else
tmp = PyList_New(bytesTotal);
#endif
if (tmp == 0)
python::throw_error_already_set();
result = python::object(python::handle<>(tmp));
}
UINT itemIndex = 0;
for (UINT y=0; y < ySpan; y += yStep) {
for (UINT x=0; x < xSpan; x += xStep) {
UINT accessIndex = ((y + yStart) * mapData.RowPitch) + ((x + xStart) * elementByteSize);
BYTE* __restrict data = (BYTE*)mapData.pData + accessIndex;
if (getter) {
for (UINT i=0; i < elementByteSize; ++i) {
#if PY_VERSION_HEX >= 0x02060000
//Write directly into the bytearray.
PyByteArray_AS_STRING(result.ptr())[itemIndex++] = *(data+i);
#else
PyObject* tmpInt = PyLong_FromLong(*(data+i));
if (tmpInt == 0)
python::throw_error_already_set();
PyList_SET_ITEM(result.ptr(), itemIndex++, tmpInt); //Steals a reference.
#endif
}
}
else {
for (UINT i=0; i < elementByteSize; ++i) {
*(data+i) = python::extract<BYTE>((*values)[itemIndex++]);
}
}
}
}
if (getter && bytesTotal != itemIndex) {
DPThrow("Internal calculation error");
}
return result;
}
python::object DPTexture::getitem(python::object& indices)
{
checkAccessRW(true, false);
return getorsetitem(indices, 0, true);
}
void DPTexture::setitem(python::object& indices, python::object& value)
{
checkAccessRW(false, true);
getorsetitem(indices, &value, false);
}
void DPTexture::map(UINT argMapType, UINT arrayIndex)
{
if (isMapped())
DPThrow("Texture is already mapped");
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
mapType = (D3D11_MAP)argMapType;
mappedSubresource = D3D11CalcSubresource(0, arrayIndex, desc.MipLevels);
HRESULT hr = getContext()->Map(texture, mappedSubresource, mapType, 0, &mapData);
TestHR(hr);
}
void DPTexture::unmap()
{
if (!isMapped())
DPThrow("Texture is not mapped");
getContext()->Unmap(texture, mappedSubresource);
mapData.pData = 0;
mappedSubresource = 0;
}
void DPTexture::checkAccessRW(bool read, bool write)
{
if (!isMapped())
DPThrow("Texture is not currently mapped");
if (read) {
if (mapType == D3D11_MAP_WRITE || mapType == D3D11_MAP_WRITE_DISCARD
|| mapType == D3D11_MAP_WRITE_NO_OVERWRITE)
DPThrow("Attempting to read from a write-only texture");
}
if (write) {
if (mapType == D3D11_MAP_READ)
DPThrow("Attempting to write to a read-only texture");
}
}
bool DPTexture::isWritable()
{
if (!isMapped())
return false;
if (mapType != D3D11_MAP_READ) {
return true;
}
return false;
}
#ifdef DP_BUFFER_SUPPORT
extern "C"
int DPTextureGetBuffer(PyObject *obj, Py_buffer *view, int flags)
{
DPTexture& self = python::extract<DPTexture&>(obj);
if (view == 0)
return 0;
ZeroMemory(view, sizeof(*view));
D3D11_TEXTURE2D_DESC desc;
self.getTexture()->GetDesc(&desc);
D3D11_MAPPED_SUBRESOURCE mapData = self.getMapData();
const UINT itemByteSize = GetFormatByteSize(desc.Format); //XXX This can throw.
if (flags & PyBUF_WRITABLE && !self.isWritable()) {
PyErr_SetString(PyExc_BufferError, "Texture was not mapped for writing");
return -1;
}
if (mapData.RowPitch != (desc.Width * itemByteSize)) {
PyErr_SetString(PyExc_BufferError, "Texture has a pitch");
return -1;
}
int readonly = 1;
if (self.isWritable())
readonly = 0;
UINT byteSize = desc.Width * desc.Height * itemByteSize;
return PyBuffer_FillInfo(view, obj, mapData.pData, byteSize, readonly, flags);
/*
int ok = PyBuffer_FillInfo(view, obj, mapData.pData, byteSize, readonly, flags);
//view->ndim = 2;
view->shape = (Py_ssize_t*)malloc(sizeof(Py_ssize_t) * 2);
view->shape[0] = desc.Width;
view->shape[1] = desc.Height;
return ok;*/
}
extern "C"
void DPTextureReleaseBuffer(PyObject *obj, Py_buffer *view)
{
DPTexture& self = python::extract<DPTexture&>(obj);
//free(view->shape);
//self.getTexture()->Release();
}
PyBufferProcs DPTextureAsBuffer;
#endif
void ExportDPTexture()
{
using namespace boost::python;
object DPTextureClass = class_<DPTexture, boost::noncopyable>("Texture",
init<const char*, python::object&, UINT, UINT, UINT, UINT, UINT>(
(arg("path"), arg("size") = python::make_tuple(0, 0, 1, 0), arg("format") = 0, arg("bind") = 0x8,
arg("usage") = 0, arg("cpuaccess") = 0, arg("miscflags") = 0) ) )
.def("release", &DPTexture::release)
.def("resolve", &DPTexture::resolve)
.def("copy", &DPTexture::copy)
.def("copyRegion", &DPTexture::copyRegion)
.def("filter", &DPTexture::filter)
.def("save", &DPTexture::save)
.def("map", &DPTexture::map, (arg("maptype"), arg("arrayindex") = 0))
.def("unmap", &DPTexture::unmap)
.def("size", &DPTexture::size)
.def("getItemSize", &DPTexture::itemSize)
.def("getItemFormat", &DPTexture::getItemFormat)
.def("getDesc", &DPTexture::description)
.def("__getitem__", &DPTexture::getitem)
.def("__setitem__", &DPTexture::setitem)
;
#ifdef DP_BUFFER_SUPPORT
DPTextureAsBuffer.bf_getbuffer = &DPTextureGetBuffer;
DPTextureAsBuffer.bf_releasebuffer = &DPTextureReleaseBuffer;
((PyTypeObject*)(DPTextureClass.ptr()))->tp_as_buffer = &DPTextureAsBuffer;
#endif
}