/*
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 "DPEffect.hpp"
#include "DPDevice.hpp"
#include "DPView.hpp"
#include "DPMatrix.hpp"
#include "DPBuffer.hpp"
DPEffect::~DPEffect()
{
release();
}
D3D10_SHADER_MACRO DPMacroDefines = {
"DP_VERSION", DPVERSIONHEXSTR
};
DPEffect::DPEffect(const char* path, python::object& macros, UINT flags)
{
effect = 0;
DPDevice* device = getDPDevice();
std::vector<D3D10_SHADER_MACRO> defines;
if (macros != python::object()) {
/* Convert a dictionary into macro structures.
XXX It is possible that these strings are freed
before D3DX11CompileFromFile() has finished!
*/
python::dict pydict(macros);
python::object items = pydict.items();
python::ssize_t size = python::len(items);
for (python::ssize_t i=0; i < size; ++i) {
D3D10_SHADER_MACRO tmp;
tmp.Name = python::extract<const char*>(items[i][0]);
tmp.Definition = python::extract<const char*>(items[i][1]);
defines.push_back(tmp);
}
}
D3D10_SHADER_MACRO terminator = {0};
defines.push_back(DPMacroDefines);
defines.push_back(terminator);
ID3D10Blob* blob = 0;
ID3D10Blob* errorBlob = 0;
HRESULT hr;
if (DPIsDebugEnabled()) {
flags |= D3D10_SHADER_DEBUG | D3D10_SHADER_SKIP_OPTIMIZATION;
}
Py_BEGIN_ALLOW_THREADS //XXX - Not totally safe.
hr = D3DX11CompileFromFileA(path, &defines[0], NULL, NULL,
"fx_5_0", flags, 0, NULL, &blob, &errorBlob, NULL);
Py_END_ALLOW_THREADS
if (FAILED(hr)) {
DPFormatter error;
error << "Effect compilation failed: ";
if (errorBlob) {
const char* errorMsg = static_cast<const char*>(errorBlob->GetBufferPointer());
if (errorMsg)
error << errorMsg;
}
SafeRelease(errorBlob);
SafeRelease(blob);
error.raise();
}
else if (errorBlob) {
const char* warnings = static_cast<const char*>(errorBlob->GetBufferPointer());
if (warnings) {
DPFormatter formatter;
formatter << "Effect compilation warning(s): " << warnings;
formatter.warn(); //XXX can leak.
}
}
SafeRelease(errorBlob);
hr = D3DX11CreateEffectFromMemory(blob->GetBufferPointer(),
blob->GetBufferSize(), 0, device->getDevice(), &effect);
SafeRelease(blob);
if (FAILED(hr)) {
DPThrow("Effect creation failed");
}
DPBase::init(device->getDevice());
}
void DPEffect::release()
{
SafeRelease(effect);
}
template<typename T>
inline T isValid(T arg, const char* errorMsg=0) {
if (!arg->IsValid()) {
DPThrow(errorMsg ? errorMsg : "Type is not valid");
}
return arg;
}
D3DX11_EFFECT_TYPE_DESC getType(ID3DX11EffectVariable* variable)
{
D3DX11_EFFECT_TYPE_DESC typeDesc;
HRESULT hr = isValid(variable->GetType(), "Invalid type info")->GetDesc(&typeDesc);
if (FAILED(hr))
DPThrow("Can't get type info");
return typeDesc;
}
void DPEffect::apply(UINT techIndex, UINT passIndex)
{
ID3DX11EffectTechnique* technique = isValid(effect->GetTechniqueByIndex(techIndex), "Invalid technique");
ID3DX11EffectPass* fxPass = isValid(technique->GetPassByIndex(passIndex), "Invalid pass");
TestHR(fxPass->Apply(0, getContext()));
}
void DPEffect::applyStr(const char* techName, UINT passIndex)
{
ID3DX11EffectTechnique* technique = isValid(effect->GetTechniqueByName(techName), "Invalid technique");
ID3DX11EffectPass* fxPass = isValid(technique->GetPassByIndex(passIndex), "Invalid pass");
TestHR(fxPass->Apply(0, getContext()));
}
void DPEffect::set(const char* varName, python::object& value)
{
if (value == python::object())
DPThrow("Can't set None");
ID3DX11EffectVariable* variable = getVariable(varName);
D3DX11_EFFECT_TYPE_DESC typeDesc = getType(variable);
if (typeDesc.Rows == 4 && typeDesc.Columns == 4 && typeDesc.Elements > 0) {
//Probably a matrix array. (Single matrix has an overload.)
boost::scoped_array<XMMATRIX> temp(new XMMATRIX[typeDesc.Elements]);
for (UINT i=0; i < typeDesc.Elements; ++i) {
DPMatrix& matrix = python::extract<DPMatrix&>(value[i]);
temp[i] = matrix.getMatrix();
}
float* data = &temp[0]._11;
TestHR(isValid(variable->AsMatrix(), "Not a matrix")->SetMatrixArray(data, 0, typeDesc.Elements));
return;
}
if (typeDesc.Rows > 1)
DPThrow("No support for row data");
if (typeDesc.Elements == 0) {
if (typeDesc.Columns == 1 && typeDesc.Rows == 1) {
//Single value, for example "float somescalar;"
TestHR(isValid(variable->AsScalar(), "Not a scalar")->SetFloat(python::extract<float>(value)));
}
else {
//Vector, for example "float3 somevector;"
if (typeDesc.Columns > 4)
DPThrow("Too many columns");
float data[4] = {0.0f};
for (UINT i = 0; i < typeDesc.Columns; ++i) {
data[i] = python::extract<float>(value[i]);
}
TestHR(isValid(variable->AsVector(), "Not a vector")->SetFloatVector(data));
}
}
else {
boost::scoped_array<float> temp(new float[typeDesc.Elements * typeDesc.Columns]);
UINT index = 0;
if (typeDesc.Columns == 1 && typeDesc.Rows == 1) {
//Array of single values, for example "float singlearray[8];"
for (UINT i=0; i < typeDesc.Elements; ++i) {
temp[index++] = python::extract<float>(value[i]);
}
TestHR(isValid(variable->AsScalar(), "Not a scalar")->SetFloatArray(&temp[0], 0, typeDesc.Elements));
}
else {
//Array of vectors, for example "float2 somearray[3];"
for (UINT i=0; i < typeDesc.Elements; ++i) {
for (UINT x=0; x < typeDesc.Columns; ++x) {
temp[index++] = python::extract<float>(value[i][x]);
}
}
TestHR(isValid(variable->AsVector(), "Not a vector")->SetFloatVectorArray(&temp[0], 0, typeDesc.Elements));
}
}
}
void DPEffect::setView(const char* varName, DPView& view)
{
ID3DX11EffectVariable* variable = getVariable(varName);
D3DX11_EFFECT_TYPE_DESC typeDesc = getType(variable);
//Both D3D10_ and D3D11_ values are used, not a bug.
switch (typeDesc.Type) {
case D3D10_SVT_RENDERTARGETVIEW:
if (view.getRenderTargetView() == 0)
DPThrow("Not a render target view");
TestHR(isValid(variable->AsRenderTargetView())->SetRenderTarget(view.getRenderTargetView()));
break;
case D3D10_SVT_DEPTHSTENCILVIEW:
if (view.getDepthStencilView() == 0)
DPThrow("Not a render target view");
TestHR(isValid(variable->AsDepthStencilView())->SetDepthStencil(view.getDepthStencilView()));
break;
case D3D11_SVT_RWBYTEADDRESS_BUFFER:
case D3D11_SVT_RWSTRUCTURED_BUFFER:
if (view.getUnorderedAccessView() == 0)
DPThrow("Not an unordered buffer view");
TestHR(isValid(variable->AsUnorderedAccessView())->SetUnorderedAccessView(view.getUnorderedAccessView()));
break;
case D3D11_SVT_BYTEADDRESS_BUFFER:
case D3D11_SVT_STRUCTURED_BUFFER:
case D3D10_SVT_TEXTURE2DMS:
case D3D10_SVT_TEXTURE2DMSARRAY:
case D3D10_SVT_TEXTURE2D:
case D3D10_SVT_TEXTURE2DARRAY:
if (view.getShaderView() == 0)
DPThrow("Not a shader resource view");
TestHR(isValid(variable->AsShaderResource())->SetResource(view.getShaderView()));
break;
default:
DPThrow("Unsupported view type");
}
}
void DPEffect::setBuffer(const char* varName, DPBuffer& buffer)
{
ID3DX11EffectConstantBuffer* fxBuffer = isValid(effect->GetConstantBufferByName(varName), "Variable is not valid");
TestHR(isValid(fxBuffer->AsConstantBuffer(), "Not a constant buffer")->SetConstantBuffer(buffer.getBuffer()));
}
void DPEffect::setMatrix(const char* varName, DPMatrix& matrix)
{
const float* constPtr = &matrix.getMatrix()._11;
TestHR(isValid(getVariable(varName)->AsMatrix(), "Not a matrix")->SetMatrix(const_cast<float*>(constPtr)));
}
void DPEffect::setRaw(const char* varName, python::object& source)
{
ID3DX11EffectVariable* variable = getVariable(varName);
const void* data = 0;
Py_ssize_t size = 0;
if (PyObject_AsReadBuffer(source.ptr(), &data, &size) == -1)
python::throw_error_already_set();
TestHR(variable->SetRawValue(const_cast<void*>(data), 0, size));
}
#define RETURN_VAR(tp, getter, arraygetter, vartype) \
if (typeDesc.Elements > 0) { \
boost::scoped_array<vartype> raw(new vartype[typeDesc.Elements]); \
TestHR(variable->tp()->arraygetter(&raw[0], 0, typeDesc.Elements)); \
python::list results; \
for (UINT i=0; i < typeDesc.Elements; ++i) { \
results.append(raw[i]); \
} \
return results; \
} \
else { \
vartype temp; \
TestHR(variable->tp()->getter(&temp)); \
return python::object(temp); \
}
python::object DPEffect::get(const char* varName)
{
ID3DX11EffectVariable* variable = getVariable(varName);
D3DX11_EFFECT_TYPE_DESC typeDesc = getType(variable);
if (typeDesc.Rows > 1)
DPThrow("No support for row data");
switch (typeDesc.Type) {
case D3D10_SVT_STRING:
RETURN_VAR(AsString, GetString, GetStringArray, const char*)
case D3D10_SVT_INT:
RETURN_VAR(AsScalar, GetInt, GetIntArray, int)
case D3D10_SVT_FLOAT:
RETURN_VAR(AsScalar, GetFloat, GetFloatArray, float)
}
DPThrow("Unsupported type");
return python::object(); //Should never get in here.
}
ID3DX11EffectVariable* DPEffect::getVariable(const char* name)
{
ID3DX11EffectVariable* variable = 0;
if (name[0] == ':')
variable = effect->GetVariableBySemantic(name + 1);
else
variable = effect->GetVariableByName(name);
if (!variable->IsValid())
DPThrow("Variable is not valid");
return variable;
}
python::list DPEffect::getTechniques()
{
D3DX11_EFFECT_DESC desc;
TestHR(effect->GetDesc(&desc));
python::list result;
for (UINT i=0; i < desc.Techniques; ++i) {
ID3DX11EffectTechnique* tech = effect->GetTechniqueByIndex(i);
D3DX11_TECHNIQUE_DESC techDesc;
TestHR(tech->GetDesc(&techDesc));
result.append(techDesc.Name);
}
return result;
}
python::list DPEffect::getPasses(UINT techIndex)
{
ID3DX11EffectTechnique* tech = isValid(effect->GetTechniqueByIndex(techIndex), "Invalid technique");
D3DX11_TECHNIQUE_DESC techDesc;
TestHR(tech->GetDesc(&techDesc));
python::list result;
for (UINT i=0; i < techDesc.Passes; ++i) {
ID3DX11EffectPass* fxPass = tech->GetPassByIndex(i);
D3DX11_PASS_DESC passDesc;
TestHR(fxPass->GetDesc(&passDesc));
result.append(passDesc.Name);
}
return result;
}
void ExportDPEffect()
{
using namespace boost::python;
class_<DPEffect, boost::noncopyable>("Effect", init<const char*, object&, UINT>(
(arg("name"), arg("defines") = object(), arg("flags") = D3D10_SHADER_ENABLE_STRICTNESS) ))
.def("release", &DPEffect::release)
.def("get", &DPEffect::get)
.def("set", &DPEffect::set)
.def("set", &DPEffect::setView)
.def("set", &DPEffect::setBuffer)
.def("set", &DPEffect::setMatrix)
.def("setRaw", &DPEffect::setRaw)
.def("apply", &DPEffect::apply)
.def("apply", &DPEffect::applyStr)
.def("getTechniques", &DPEffect::getTechniques)
.def("getPasses", &DPEffect::getPasses)
;
}