My MVCE for SSL relay server:
#pragma once
#include <stdint.h>
#include <iostream>
#include <asio.hpp>
#include <asio/ssl.hpp>
namespace test
{
namespace setup
{
const uint32_t maxMessageSize = 1024 * 1024;
const uint32_t maxSessionsNum = 10;
}
enum class MessageType
{
LOG_ON = 0,
TEXT_MESSAGE = 1
};
class MessageHeader
{
public:
uint32_t messageType;
uint32_t messageLength;
MessageHeader(uint32_t messageType, uint32_t messageLength) : messageType(messageType), messageLength(messageLength) {}
};
class LogOn
{
public:
MessageHeader header;
uint32_t sessionId;
uint32_t isClient0;
LogOn() : header((uint32_t)MessageType::LOG_ON, sizeof(LogOn)) {}
};
class TextMessage
{
public:
MessageHeader header;
uint8_t data[];
TextMessage() : header((uint32_t)MessageType::TEXT_MESSAGE, sizeof(TextMessage)){}
};
class ClientSocket;
class Session
{
public:
ClientSocket* pClient0;
ClientSocket* pClient1;
};
Session* getSession(uint32_t sessionId);
class ClientSocket
{
public:
bool useTLS;
std::shared_ptr<asio::ip::tcp::socket> socket;
std::shared_ptr<asio::ssl::stream<asio::ip::tcp::socket>> socketSSL;
Session* pSession;
bool isClient0;
std::recursive_mutex writeBufferLock;
std::vector<char> readBuffer;
uint32_t readPos;
ClientSocket(asio::ip::tcp::socket& socket) : useTLS(false)
{
this->socket = std::make_shared<asio::ip::tcp::socket>(std::move(socket));
this->readBuffer.resize(setup::maxMessageSize + sizeof(MessageHeader));
this->readPos = 0;
}
ClientSocket(asio::ssl::stream<asio::ip::tcp::socket>& socket) : useTLS(true)
{
this->socketSSL = std::make_shared<asio::ssl::stream<asio::ip::tcp::socket>>(std::move(socket));
this->readBuffer.resize(setup::maxMessageSize + sizeof(MessageHeader));
this->readPos = 0;
}
bool writeSocket(uint8_t* pBuffer, uint32_t bufferSize)
{
try
{
std::unique_lock<std::recursive_mutex>
lock(this->writeBufferLock);
size_t writtenBytes = 0;
if (true == this->useTLS)
{
writtenBytes = asio::write(*this->socketSSL,
asio::buffer(pBuffer, bufferSize));
}
else
{
writtenBytes = asio::write(*this->socket,
asio::buffer(pBuffer, bufferSize));
}
return (writtenBytes == bufferSize);
}
catch (asio::system_error e)
{
std::cout << e.what() << std::endl;
}
catch (std::exception e)
{
std::cout << e.what() << std::endl;
}
catch (...)
{
std::cout << "Some other exception" << std::endl;
}
return false;
}
void asyncReadNextMessage(uint32_t messageSize)
{
auto readMessageLambda = [&](const asio::error_code errorCode, std::size_t length)
{
this->readPos += (uint32_t)length;
if (0 != errorCode.value())
{
//send socket to remove
printf("errorCode= %u, message=%s\n", errorCode.value(), errorCode.message().c_str());
//sendRemoveMeSignal();
return;
}
if ((this->readPos < sizeof(MessageHeader)))
{
asyncReadNextMessage(sizeof(MessageHeader) - this->readPos);
return;
}
MessageHeader* pMessageHeader = (MessageHeader*)this->readBuffer.data();
if (pMessageHeader->messageLength > setup::maxMessageSize)
{
//Message to big - should disconnect ?
this->readPos = 0;
asyncReadNextMessage(sizeof(MessageHeader));
return;
}
if (this->readPos < pMessageHeader->messageLength)
{
asyncReadNextMessage(pMessageHeader->messageLength - this->readPos);
return;
}
MessageType messageType = (MessageType)pMessageHeader->messageType;
switch(messageType)
{
case MessageType::LOG_ON:
{
LogOn* pLogOn = (LogOn*)pMessageHeader;
printf("LOG_ON message sessionId=%u, isClient0=%u\n", pLogOn->sessionId, pLogOn->isClient0);
this->isClient0 = pLogOn->isClient0;
this->pSession = getSession(pLogOn->sessionId);
if (this->isClient0)
this->pSession->pClient0 = this;
else
this->pSession->pClient1 = this;
}
break;
case MessageType::TEXT_MESSAGE:
{
TextMessage* pTextMessage = (TextMessage*)pMessageHeader;
if (nullptr != pSession)
{
if (this->isClient0)
{
if (nullptr != pSession->pClient1)
{
pSession->pClient1->writeSocket((uint8_t*)pTextMessage, pTextMessage->header.messageLength);
}
}
else
{
if (nullptr != pSession->pClient0)
{
pSession->pClient0->writeSocket((uint8_t*)pTextMessage, pTextMessage->header.messageLength);
}
}
}
}
break;
}
this->readPos = 0;
asyncReadNextMessage(sizeof(MessageHeader));
};
if (true == this->useTLS)
{
this->socketSSL->async_read_some(asio::buffer(this->readBuffer.data() + this->readPos, messageSize), readMessageLambda);
}
else
{
this->socket->async_read_some(asio::buffer(this->readBuffer.data() + this->readPos, messageSize), readMessageLambda);
}
}
};
class SSLRelayServer
{
public:
static SSLRelayServer* pSingleton;
asio::io_context ioContext;
asio::ssl::context sslContext;
std::vector<std::thread> workerThreads;
asio::ip::tcp::acceptor* pAcceptor;
asio::ip::tcp::endpoint* pEndpoint;
bool useTLS;
Session* sessions[setup::maxSessionsNum];
SSLRelayServer() : pAcceptor(nullptr), pEndpoint(nullptr), sslContext(asio::ssl::context::tlsv13_server)//sslContext(asio::ssl::context::sslv23)
{
this->useTLS = false;
this->pSingleton = this;
//this->sslContext.set_options(asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2);
this->sslContext.set_password_callback(std::bind(&SSLRelayServer::getPrivateKeyPEMFilePassword, this));
this->sslContext.use_certificate_chain_file("server_cert.pem");
this->sslContext.use_private_key_file("server_private_key.pem",
asio::ssl::context::pem);
}
static SSLRelayServer* getSingleton()
{
return pSingleton;
}
std::string getPrivateKeyPEMFilePassword() const
{
return "";
}
void addClientSocket(asio::ip::tcp::socket& socket)
{
ClientSocket* pClientSocket = new ClientSocket(socket); // use smart pointers
pClientSocket->asyncReadNextMessage(sizeof(MessageHeader));
}
void addSSLClientToken(asio::ssl::stream<asio::ip::tcp::socket>&sslSocket)
{
ClientSocket* pClientSocket = new ClientSocket(sslSocket); // use smart pointers
pClientSocket->asyncReadNextMessage(sizeof(MessageHeader));
}
void handleAccept(asio::ip::tcp::socket& socket, const asio::error_code& errorCode)
{
if (!errorCode)
{
printf("accepted\n");
if (true == socket.is_open())
{
asio::ip::tcp::no_delay no_delay_option(true);
socket.set_option(no_delay_option);
addClientSocket(socket);
}
}
}
void handleAcceptTLS(asio::ip::tcp::socket& socket, const asio::error_code& errorCode)
{
if (!errorCode)
{
printf("accepted\n");
if (true == socket.is_open())
{
asio::ip::tcp::no_delay no_delay_option(true);
asio::ssl::stream<asio::ip::tcp::socket> sslStream(std::move(socket), this->sslContext);
try
{
sslStream.handshake(asio::ssl::stream_base::server);
sslStream.lowest_layer().set_option(no_delay_option);
addSSLClientToken(sslStream);
}
catch (asio::system_error e)
{
std::cout << e.what() << std::endl;
return;
}
catch (std::exception e)
{
std::cout << e.what() << std::endl;
return;
}
catch (...)
{
std::cout << "Other exception" << std::endl;
return;
}
}
}
}
void startAccept()
{
auto acceptHandler = [this](const asio::error_code& errorCode, asio::ip::tcp::socket socket)
{
printf("acceptHandler\n");
handleAccept(socket, errorCode);
this->startAccept();
};
auto tlsAcceptHandler = [this](const asio::error_code& errorCode, asio::ip::tcp::socket socket)
{
printf("tlsAcceptHandler\n");
handleAcceptTLS(socket, errorCode);
this->startAccept();
};
if (true == this->useTLS)
{
this->pAcceptor->async_accept(tlsAcceptHandler);
}
else
{
this->pAcceptor->async_accept(acceptHandler);
}
}
bool run(uint32_t servicePort, uint32_t threadsNum, bool useTLS)
{
this->useTLS = useTLS;
this->pEndpoint = new asio::ip::tcp::endpoint(asio::ip::tcp::v4(), servicePort);
this->pAcceptor = new asio::ip::tcp::acceptor(ioContext, *pEndpoint);
this->pAcceptor->listen();
this->startAccept();
for (uint32_t threadIt = 0; threadIt < threadsNum; ++threadIt)
{
this->workerThreads.emplace_back([&]() {
#ifdef WINDOWS
SetThreadDescription(GetCurrentThread(), L"SSLRelayServer worker thread");
#endif
this->ioContext.run(); }
);
}
return true;
}
Session* getSession(uint32_t sessionId)
{
if (nullptr == this->sessions[sessionId])
{
this->sessions[sessionId] = new Session();
}
return this->sessions[sessionId];
}
};
SSLRelayServer* SSLRelayServer::pSingleton = nullptr;
Session* getSession(uint32_t sessionId)
{
SSLRelayServer* pServer = SSLRelayServer::getSingleton();
Session* pSession = pServer->getSession(sessionId);
return pSession;
}
class Client
{
public:
asio::ssl::context sslContext;
std::shared_ptr<asio::ip::tcp::socket> socket;
std::shared_ptr<asio::ssl::stream<asio::ip::tcp::socket>> socketSSL;
asio::io_context ioContext;
bool useTLS;
bool isClient0;
uint32_t readDataIt;
std::vector<uint8_t> readBuffer;
std::thread listenerThread;
Client() : sslContext(asio::ssl::context::tlsv13_client)//sslContext(asio::ssl::context::sslv23)
{
sslContext.load_verify_file("server_cert.pem");
//sslContext.set_verify_mode(asio::ssl::verify_peer);
using asio::ip::tcp;
using std::placeholders::_1;
using std::placeholders::_2;
sslContext.set_verify_callback(std::bind(&Client::verifyCertificate, this, _1, _2));
this->readBuffer.resize(setup::maxMessageSize);
this->readDataIt = 0;
}
bool verifyCertificate(bool preverified, asio::ssl::verify_context& verifyCtx)
{
return true;
}
void listenerRunner()
{
#ifdef WINDOWS
if (this->isClient0)
{
SetThreadDescription(GetCurrentThread(), L"listenerRunner client0");
}
else
{
SetThreadDescription(GetCurrentThread(), L"listenerRunner client1");
}
#endif
while (1==1)
{
asio::error_code errorCode;
size_t transferred = 0;
if (true == this->useTLS)
{
transferred = this->socketSSL->read_some(asio::buffer(this->readBuffer.data() + this->readDataIt, sizeof(MessageHeader) - this->readDataIt), errorCode);
}
else
{
transferred = this->socket->read_some(asio::buffer(this->readBuffer.data() + this->readDataIt, sizeof(MessageHeader) - this->readDataIt), errorCode);
}
this->readDataIt += transferred;
if (0 != errorCode.value())
{
this->readDataIt = 0;
continue;
}
if (this->readDataIt < sizeof(MessageHeader))
continue;
MessageHeader* pMessageHeader = (MessageHeader*)this->readBuffer.data();
if (pMessageHeader->messageLength > setup::maxMessageSize)
{
exit(1);
}
bool resetSocket = false;
while (pMessageHeader->messageLength > this->readDataIt)
{
printf("readDataIt=%u, threadId=%u\n", this->readDataIt, GetCurrentThreadId());
{
//message not complete
if (true == this->useTLS)
{
transferred = this->socketSSL->read_some(asio::buffer(this->readBuffer.data() + this->readDataIt, pMessageHeader->messageLength - this->readDataIt), errorCode);
}
else
{
transferred = this->socket->read_some(asio::buffer(this->readBuffer.data() + this->readDataIt, pMessageHeader->messageLength - this->readDataIt), errorCode);
}
this->readDataIt += transferred;
}
if (0 != errorCode.value())
{
exit(1);
}
}
MessageType messageType = (MessageType)pMessageHeader->messageType;
switch (messageType)
{
case MessageType::TEXT_MESSAGE:
{
TextMessage* pTextMessage = (TextMessage*)pMessageHeader;
printf("TEXT_MESSAGE: %s\n", pTextMessage->data);
}
break;
}
this->readDataIt = 0;
}
}
void run(uint32_t sessionId, bool isClient0, bool useTLS, uint32_t servicePort)
{
this->useTLS = useTLS;
this->isClient0 = isClient0;
if (useTLS)
{
socketSSL = std::make_shared<asio::ssl::stream<asio::ip::tcp::socket>>(ioContext, sslContext);
}
else
{
socket = std::make_shared<asio::ip::tcp::socket>(ioContext);
}
asio::ip::tcp::resolver resolver(ioContext);
asio::ip::tcp::resolver::results_type endpoints = resolver.resolve(asio::ip::tcp::v4(), "127.0.0.1", std::to_string(servicePort));
asio::ip::tcp::no_delay no_delay_option(true);
if (true == useTLS)
{
asio::ip::tcp::endpoint sslEndpoint = asio::connect(socketSSL->lowest_layer(), endpoints);
socketSSL->handshake(asio::ssl::stream_base::client);
socketSSL->lowest_layer().set_option(no_delay_option);
}
else
{
asio::ip::tcp::endpoint endpoint = asio::connect(*socket, endpoints);
socket->set_option(no_delay_option);
}
this->listenerThread = std::thread(&Client::listenerRunner, this);
LogOn logOn;
logOn.isClient0 = isClient0;
logOn.sessionId = sessionId;
const uint32_t logOnSize = sizeof(logOn);
if (true == useTLS)
{
size_t transferred = asio::write(*socketSSL, asio::buffer(&logOn, sizeof(LogOn)));
}
else
{
size_t transferred = asio::write(*socket, asio::buffer(&logOn, sizeof(LogOn)));
}
uint32_t counter = 0;
while (1 == 1)
{
std::string number = std::to_string(counter);
std::string message;
if (this->isClient0)
{
message = "Client0: " + number;
}
else
{
message = "Client1: " + number;
}
TextMessage textMessage;
textMessage.header.messageLength += message.size() + 1;
if (this->useTLS)
{
size_t transferred = asio::write(*socketSSL, asio::buffer(&textMessage, sizeof(TextMessage)));
transferred = asio::write(*socketSSL, asio::buffer(message.c_str(), message.length() + 1));
}
else
{
size_t transferred = asio::write(*socket, asio::buffer(&textMessage, sizeof(TextMessage)));
transferred = asio::write(*socket, asio::buffer(message.c_str(), message.length() + 1));
}
++counter;
//Sleep(1000);
}
}
};
void clientTest(uint32_t sessionId, bool isClient0, bool useTLS,
uint32_t servicePort)
{
#ifdef WINDOWS
if (isClient0)
{
SetThreadDescription(GetCurrentThread(), L"Client0");
}
else
{
SetThreadDescription(GetCurrentThread(), L"Client1");
}
#endif
Client client;
client.run(sessionId, isClient0, useTLS, servicePort);
while (1 == 1)
{
Sleep(1000);
}
}
void SSLRelayTest()
{
SSLRelayServer relayServer;
const uint32_t threadsNum = 1;
const bool useTLS = true;
const uint32_t servicePort = 777;
relayServer.run(servicePort, threadsNum, useTLS);
Sleep(5000);
std::vector<std::thread> threads;
const uint32_t sessionId = 0;
threads.emplace_back(clientTest, sessionId, true, useTLS, servicePort);
threads.emplace_back(clientTest, sessionId, false, useTLS,servicePort);
for (std::thread& threadIt : threads)
{
threadIt.join();
}
}
}
What this sample does ?
It runs SSL relay server on localhost port 777 which connects two clients and allows exchanging
of text messages between them.
Promblem:
When I run that sample server returns error "errorCode= 167772441, message=decryption failed or bad record mac (SSL routines)" in void "asyncReadNextMessage(uint32_t messageSize)"
I found out this is caused by client which reads and writes to client SSL socket from separate threads (changing variable useTLS to 0 runs it on normal socket which proves that it is SSL socket problem).
Apparently TLS is not full-duplex protocol (I did not know about that). I can't synchronize access to read and write with mutex because when socket enters read state and there is no
incoming message writing to socked will be blocked forever. At this thread Boost ASIO, SSL: How do strands help the implementation?
someone recommended using strands but someone else wrote that asio only synchronizes not concurrent execution of read and write handles which does not fix the problem.
I expect that somehow there is a way to synchronize read and write to SSL socket. I'm 100% sure that problem lies in synchronizing read and writes to socket because when I wrote example with read and write to socket done by one thread it worked. However then client always expects that there is message to read which can block all write if there is not. Can it be solved without using separate sockets for reads and writes ?
Okay I figured it out by writting many diffrent samples of code including SSL sockets.
When asio::io_context is already running you can't simply schedule asio::async_write or asio::async_read from thread which is not
associated with strand connected to that socket.
So when there is:
asio::async_write(*this->socketSSL, asio::buffer(pBuffer, bufferSize), asio::bind_executor(readWriteStrand,writeMessageLambda));
but thread which is executing is not running from readWriteStrand strand then it should be written as:
asio::post(ioContext, asio::bind_executor(readWriteStrand, [&]() {asio::async_read(*this->socketSSL, asio::buffer(readBuffer.data() + this->readDataIt, messageSize), asio::bind_executor(readWriteStrand, readMessageLambda)); }));
Just a warning, I'm new and inexperienced in C++ so if I have really bad syntax or I'm missing something obvious, please excuse me. I'm trying to make a program that allows me to open other programs (like game launchers) from inside of it.
I don't want to define the programs in the code so I can change it without recompiling. I'm using a Visual Studio generated Windows Form header file that I've made a few tweaks to and a main cpp file. When I go to compile, I get an ERR_MESSAGE saying that my functions already have bodies. Here's my code:
CPP Script MyForm.cpp:
#include "MyForm.h"
#include <iostream> // Debug (Remove once done)
#include <fstream> // File stream
#include <stdlib.h> // Execute files
using namespace std;
using namespace System;
using namespace System::Windows::Forms;
// Creating the virtual file arrays
char line1[];
char line2[];
char line3[];
char line4[];
char line5[];
char line6[];
char line7[];
char line8[];
char line9[];
char line10[];
char line11[];
char line12[];
namespace Loader {
void loadProgram(int num) {
char new_line[] = { '\n' };
if (num == 1) {
if (line2 != new_line) {
system(line2);
}
}
else if (num == 2) {
if (line4 != new_line) {
system(line4);
}
}
else if (num == 3) {
if (line6 != new_line) {
system(line6);
}
}
else if (num == 4) {
if (line8 != new_line) {
system(line8);
}
}
else if (num == 5) {
if (line10 != new_line) {
system(line10);
}
}
else if (num == 6) {
if (line12 != new_line) {
system(line12);
}
}
}
}
[STAThreadAttribute]
int main() {
// Assigning variables made in pre-processing
const int lineLength = 255;
if (!lineLength) {
const int lineLength = 255;
}
unsigned int lineLengthUnsigned = lineLength;
char line1[lineLength];
char line2[lineLength];
char line3[lineLength];
char line4[lineLength];
char line5[lineLength];
char line6[lineLength];
char line7[lineLength];
char line8[lineLength];
char line9[lineLength];
char line10[lineLength];
char line11[lineLength];
char line12[lineLength];
ifstream file("Programs.txt"); // Load programs list file (READ ONLY)
// Odd lines are program names, even lines are program shortcuts
file.getline(line1, lineLength);
file.getline(line2, lineLength);
file.getline(line3, lineLength);
file.getline(line4, lineLength);
file.getline(line5, lineLength);
file.getline(line6, lineLength);
file.getline(line7, lineLength);
file.getline(line8, lineLength);
file.getline(line9, lineLength);
file.getline(line10, lineLength);
file.getline(line11, lineLength);
file.getline(line12, lineLength);
// Below will run the window
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
FileOpener::MyForm form;
Application::Run(%form);
}
Header File MyForm.h:
#pragma once
#include "MyForm.cpp"
using namespace System;
using namespace System::Windows::Forms;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Data;
using namespace System::Drawing;
namespace FileOpener {
/// <summary>
/// Summary for MyForm
/// </summary>
public ref class MyForm : public System::Windows::Forms::Form
{
public:
MyForm(void)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
}
protected:
/// <summary>
/// Clean up any resources being used.
/// </summary>
~MyForm()
{
if (components)
{
delete components;
}
}
private: System::Windows::Forms::Button^ button1;
private: System::Windows::Forms::Button^ button2;
private: System::Windows::Forms::Button^ button3;
private: System::Windows::Forms::Button^ button4;
private: System::Windows::Forms::Button^ button5;
private: System::Windows::Forms::Button^ button6;
protected:
private:
/// <summary>
/// Required designer variable.
/// </summary>
System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
void InitializeComponent(void)
{
System::ComponentModel::ComponentResourceManager^ resources = (gcnew System::ComponentModel::ComponentResourceManager(MyForm::typeid));
this->button1 = (gcnew System::Windows::Forms::Button());
this->button2 = (gcnew System::Windows::Forms::Button());
this->button3 = (gcnew System::Windows::Forms::Button());
this->button4 = (gcnew System::Windows::Forms::Button());
this->button5 = (gcnew System::Windows::Forms::Button());
this->button6 = (gcnew System::Windows::Forms::Button());
this->SuspendLayout();
//
// button1
//
this->button1->Location = System::Drawing::Point(12, 12);
this->button1->Name = L"button1";
this->button1->Size = System::Drawing::Size(64, 64);
this->button1->TabIndex = 0;
this->button1->Text = L"button1";
this->button1->UseVisualStyleBackColor = true;
this->button1->Click += gcnew System::EventHandler(this, &MyForm::button1_Click);
//
// button2
//
this->button2->Location = System::Drawing::Point(82, 12);
this->button2->Name = L"button2";
this->button2->Size = System::Drawing::Size(64, 64);
this->button2->TabIndex = 1;
this->button2->Text = L"button2";
this->button2->UseVisualStyleBackColor = true;
this->button2->Click += gcnew System::EventHandler(this, &MyForm::button2_Click);
//
// button3
//
this->button3->Location = System::Drawing::Point(152, 12);
this->button3->Name = L"button3";
this->button3->Size = System::Drawing::Size(64, 64);
this->button3->TabIndex = 2;
this->button3->Text = L"button3";
this->button3->UseVisualStyleBackColor = true;
this->button3->Click += gcnew System::EventHandler(this, &MyForm::button3_Click);
//
// button4
//
this->button4->Location = System::Drawing::Point(12, 82);
this->button4->Name = L"button4";
this->button4->Size = System::Drawing::Size(64, 64);
this->button4->TabIndex = 3;
this->button4->Text = L"button4";
this->button4->UseVisualStyleBackColor = true;
this->button4->Click += gcnew System::EventHandler(this, &MyForm::button4_Click);
//
// button5
//
this->button5->Location = System::Drawing::Point(82, 82);
this->button5->Name = L"button5";
this->button5->Size = System::Drawing::Size(64, 64);
this->button5->TabIndex = 4;
this->button5->Text = L"button5";
this->button5->UseVisualStyleBackColor = true;
this->button5->Click += gcnew System::EventHandler(this, &MyForm::button5_Click);
//
// button6
//
this->button6->Location = System::Drawing::Point(152, 82);
this->button6->Name = L"button6";
this->button6->Size = System::Drawing::Size(64, 64);
this->button6->TabIndex = 5;
this->button6->Text = L"button6";
this->button6->UseVisualStyleBackColor = true;
this->button6->Click += gcnew System::EventHandler(this, &MyForm::button6_Click);
//
// MyForm
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->BackColor = System::Drawing::Color::Black;
this->BackgroundImage = (cli::safe_cast<System::Drawing::Image^>(resources->GetObject(L"$this.BackgroundImage")));
this->ClientSize = System::Drawing::Size(228, 157);
this->Controls->Add(this->button6);
this->Controls->Add(this->button5);
this->Controls->Add(this->button4);
this->Controls->Add(this->button3);
this->Controls->Add(this->button2);
this->Controls->Add(this->button1);
this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedSingle;
this->Icon = (cli::safe_cast<System::Drawing::Icon^>(resources->GetObject(L"$this.Icon")));
this->Name = L"MyForm";
this->Text = L"File Opener";
this->Load += gcnew System::EventHandler(this, &MyForm::MyForm_Load);
this->ResumeLayout(false);
}
private: System::Void MyForm_Load(System::Object^ sender, System::EventArgs^ e) {
}
public: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
Loader::loadProgram(1);
}
public: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) {
Loader::loadProgram(2);
}
public: System::Void button3_Click(System::Object^ sender, System::EventArgs^ e) {
Loader::loadProgram(3);
}
public: System::Void button4_Click(System::Object^ sender, System::EventArgs^ e) {
Loader::loadProgram(4);
}
public: System::Void button5_Click(System::Object^ sender, System::EventArgs^ e) {
Loader::loadProgram(5);
}
public: System::Void button6_Click(System::Object^ sender, System::EventArgs^ e) {
Loader::loadProgram(6);
}
};
}
#pragma endregion
Again, sorry for any facepalm-worthy parts in my code or this post, as this is my first post. Any pointers/tips would be appreciated!
I was able to fix the functions already have bodies error by using PeterT's suggestion.
I'm trying to use DWrite to draw text in my dx11 app but I'm having lot of problems, I shared resources beetween D3D10 Device and D3D11 Device because dx10 is capable to connect D3D with D2D, that's the code of my GraphicsDevice:
// File: GraphicsDevice.h
#pragma once
#ifndef _GRAPHICSDEVICE_H_
#define _GRAPHICSDEVICE_H_
#ifndef _DEFINES_H_
#include "Defines.h"
#endif
#ifndef _COLOR_H_
#include "Color.h"
#endif
#ifndef _UTILITIES_H_
#include "Utilities.h"
#endif
#ifndef _DWRITE_H_
#include "DWrite.h"
#endif
// Forward declaration
namespace BSGameFramework { ref class Game; }
using namespace BSGameFramework;
using namespace System;
namespace BSGameFramework
{
namespace Graphics
{
public ref class GraphicsDevice
{
public:
/// <summary>GraphicsDevice constructor.</summary>
/// <param name="game">The game wich the device has to work.</param>
GraphicsDevice(Game^ game);
virtual ~GraphicsDevice();
/// <summary>Clear the screen.</summary>
/// <param name="color">The color that the screen background will assume after clearing.</param>
void Clear(Color color);
/// <summary>Render on the screen.</summary>
void Render();
/// <summary>Set the full screen state.</summary>
void FullScreen(bool isFullScreen);
property Color BlendFactor
{
Color get()
{
return blendFactor_;
}
void set(Color blendFactor_)
{
blendFactor_ = BlendFactor;
}
}
property D3D_DRIVER_TYPE DriverType
{
D3D_DRIVER_TYPE get()
{
return driverType_;
}
}
property D3D_FEATURE_LEVEL FeatureLevel
{
D3D_FEATURE_LEVEL get()
{
return featureLevel_;
}
}
property ID3D11Device* D3DDevice
{
ID3D11Device* get()
{
return d3dDevice_;
}
}
property ID3D11DeviceContext* D3DContext
{
ID3D11DeviceContext* get()
{
return d3dContext_;
}
}
property ID3D10Device1* D3DDevice10_1
{
ID3D10Device1* get()
{
return d3dDevice10_1_;
}
}
property ID2D1Factory* D2DFactory
{
ID2D1Factory* get()
{
return d2dFactory_;
}
}
property ID2D1RenderTarget* D2DRenderTarget
{
ID2D1RenderTarget* get()
{
return d2dRenderTarget_;
}
}
property ID2D1SolidColorBrush* D2DSolidColorBrush
{
ID2D1SolidColorBrush* get()
{
return d2dSolidColorBrush_;
}
}
property IDWriteFactory* DWriteFactory
{
IDWriteFactory* get()
{
return dWriteFactory_;
}
}
property int WindowWidth
{
int get()
{
return GetWindowWidth();
}
}
property int WindowHeight
{
int get()
{
return GetWindowHeight();
}
}
property HWND Window
{
HWND get()
{
return GetWindow();
}
}
property int SafeTitleArea
{
int get()
{
return safeTitleArea_;
}
void set(int safeTitleArea)
{
safeTitleArea_ = safeTitleArea;
}
}
private:
void CreateD3D11Resources();
void CreateD3D10Resources(ID3D11Texture2D* d3d11Texture);
void CreateD2D1Resources(ID3D10Texture2D* d3d10Texture);
Game^ game_;
Color blendFactor_;
D3D_DRIVER_TYPE driverType_;
D3D_FEATURE_LEVEL featureLevel_;
int safeTitleArea_;
int GetWindowWidth();
int GetWindowHeight();
HWND GetWindow();
// Direct3D 11
ID3D11Device* d3dDevice_;
ID3D11DeviceContext* d3dContext_;
// Direct3D 10
ID3D10Device1* d3dDevice10_1_;
// Direct2D
ID2D1Factory* d2dFactory_;
ID2D1RenderTarget* d2dRenderTarget_;
ID2D1SolidColorBrush* d2dSolidColorBrush_;
// DirectWrite
IDWriteFactory* dWriteFactory_;
IDXGISwapChain* swapChain_;
ID3D11RenderTargetView* backBufferTarget_;
};
}
}
#endif
// FILE: GraphicsDevice.cpp
#include "GraphicsDevice.h"
#include "Game.h"
#include "GraphicsDeviceNativeWrapper.h"
using namespace BSGameFramework::Graphics;
using namespace BSGameFramework;
inline GraphicsDevice::GraphicsDevice(Game^ game) : driverType_( D3D_DRIVER_TYPE_NULL ), featureLevel_( D3D_FEATURE_LEVEL_11_0 ),
d3dDevice_( 0 ), d3dContext_( 0 ), swapChain_( 0 ), backBufferTarget_( 0 )
{
game_ = game;
BlendFactor = Color::White;
CreateD3D11Resources();
}
inline GraphicsDevice::~GraphicsDevice()
{
if (backBufferTarget_)
{
backBufferTarget_->Release();
}
if (swapChain_)
{
swapChain_->Release();
}
if (d3dContext_)
{
d3dContext_->Release();
}
if (d3dDevice_)
{
d3dDevice_->Release();
}
backBufferTarget_ = 0;
swapChain_ = 0;
d3dContext_ = 0;
d3dDevice_ = 0;
}
inline void GraphicsDevice::Clear(Color color)
{
if (d3dContext_ == 0)
{
return;
}
float clearColor[4];
Vec4 convertedColor = Utilities::ColorToVec4(color);
clearColor[0] = convertedColor.values[0];
clearColor[1] = convertedColor.values[1];
clearColor[2] = convertedColor.values[2];
clearColor[3] = convertedColor.values[3];
d3dContext_->ClearRenderTargetView(backBufferTarget_, clearColor);
}
inline void GraphicsDevice::Render()
{
swapChain_->Present(0, 0);
}
inline void GraphicsDevice::FullScreen(bool isFullScreen)
{
swapChain_->SetFullscreenState(isFullScreen, NULL);
}
inline int GraphicsDevice::GetWindowWidth()
{
return game_->WindowWidth;
}
inline int GraphicsDevice::GetWindowHeight()
{
return game_->WindowHeight;
}
inline HWND GraphicsDevice::GetWindow()
{
return game_->Window;
}
#pragma region CreateD3D11Resources
inline void GraphicsDevice::CreateD3D11Resources()
{
HRESULT result;
RECT dimensions;
GetClientRect(Window, &dimensions);
unsigned int width = dimensions.right - dimensions.left;
unsigned int height = dimensions.bottom - dimensions.top;
D3D_DRIVER_TYPE driverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,
D3D_DRIVER_TYPE_SOFTWARE
};
unsigned int totalDriverTypes = ARRAYSIZE(driverTypes);
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
unsigned int totalFeatureLevels = ARRAYSIZE(featureLevels);
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
swapChainDesc.BufferCount = 2;
swapChainDesc.BufferDesc.Width = width;
swapChainDesc.BufferDesc.Height = height;
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.OutputWindow = game_->Window;
swapChainDesc.Windowed = true;
swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
unsigned int creationFlags = 0;
#ifdef _DEBUG
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
unsigned int driver = 0;
pin_ptr<IDXGISwapChain*> swapChainPointer;
swapChainPointer = &swapChain_;
pin_ptr<ID3D11Device*> d3dDevicePointer;
d3dDevicePointer = &d3dDevice_;
pin_ptr<D3D_FEATURE_LEVEL> featureLevelPointer;
featureLevelPointer = &featureLevel_;
pin_ptr<ID3D11DeviceContext*> d3dContextPointer;
d3dContextPointer = &d3dContext_;
for (driver = 0; driver < totalDriverTypes; ++driver)
{
result = D3D11CreateDeviceAndSwapChain(0, driverTypes[driver], 0, creationFlags, featureLevels, totalFeatureLevels,
D3D11_SDK_VERSION, &swapChainDesc, swapChainPointer,
d3dDevicePointer, featureLevelPointer, d3dContextPointer);
if (SUCCEEDED(result))
{
driverType_ = driverTypes[driver];
break;
}
}
if (FAILED(result))
{
DXTRACE_MSG("Failed to create the Direct3D device!");
return;
}
ID3D11Texture2D* backBufferTexture;
result = swapChain_->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBufferTexture);
if (FAILED(result))
{
DXTRACE_MSG("Failed to get the swap chain back buffer!");
return;
}
pin_ptr<ID3D11RenderTargetView*> backBufferTargetPointer;
backBufferTargetPointer = &backBufferTarget_;
result = d3dDevice_->CreateRenderTargetView(backBufferTexture, 0, backBufferTargetPointer);
if (FAILED(result))
{
DXTRACE_MSG("Failed to create the render target view!");
return;
}
d3dContext_->OMSetRenderTargets(1, backBufferTargetPointer, 0);
D3D11_VIEWPORT viewport;
viewport.Width = static_cast<float>(width);
viewport.Height = static_cast<float>(height);
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
viewport.TopLeftX = 0.0f;
viewport.TopLeftY = 0.0f;
d3dContext_->RSSetViewports(1, &viewport);
CreateD3D10Resources(backBufferTexture);
}
#pragma endregion
#pragma region CreateD3D10Resources
inline void GraphicsDevice::CreateD3D10Resources(ID3D11Texture2D* d3d11Texture)
{
//Load D3D10.DLL
HMODULE d3d10_1 = LoadLibrary("D3D10_1.dll");
// Get adapter of the current D3D11 device. Our D3D10 will run on the same adapter.
IDXGIDevice* dxgiDevice;
IDXGIAdapter* dxgiAdapter;
d3dDevice_->QueryInterface<IDXGIDevice>(&dxgiDevice);
dxgiDevice->GetAdapter(&dxgiAdapter);
SafeRelease<IDXGIDevice>(&dxgiDevice);
//Get address of the function D3D10CreateDevice1 dynamically.
typedef HRESULT (WINAPI* FN_D3D10CreateDevice1)(
IDXGIAdapter *pAdapter, D3D10_DRIVER_TYPE DriverType, HMODULE Software,
UINT Flags, D3D10_FEATURE_LEVEL1 HardwareLevel, UINT SDKVersion, ID3D10Device1 **ppDevice );
FN_D3D10CreateDevice1 fnCreate = (FN_D3D10CreateDevice1)GetProcAddress(d3d10_1, "D3D10CreateDevice1");
//Call D3D10CreateDevice1 dynamically.
pin_ptr<ID3D10Device1*> d3dDevice10_1Ptr = &d3dDevice10_1_;
fnCreate(dxgiAdapter, D3D10_DRIVER_TYPE_HARDWARE, NULL, D3D10_CREATE_DEVICE_BGRA_SUPPORT | D3D10_CREATE_DEVICE_DEBUG, D3D10_FEATURE_LEVEL_10_1, D3D10_1_SDK_VERSION, d3dDevice10_1Ptr);
//Create a D3D10.1 render target texture and share it with our D3D11.
D3D10_TEXTURE2D_DESC tDesc;
tDesc.Width = game_->WindowWidth;
tDesc.Height = game_->WindowHeight;
tDesc.MipLevels = 1;
tDesc.ArraySize = 1;
tDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
tDesc.SampleDesc.Count = 1;
tDesc.SampleDesc.Quality = 0;
tDesc.Usage = D3D10_USAGE_DEFAULT;
//EVEN IF YOU WON'T USE AS SHADER RESOURCE, SET THIS BIND FLAGS:
tDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
tDesc.CPUAccessFlags = 0;
tDesc.MiscFlags = D3D10_RESOURCE_MISC_SHARED;
//Create the RT texture on D3D10
ID3D10Texture2D* texture;
d3dDevice10_1_->CreateTexture2D(&tDesc, NULL, &texture);
//Get DXGI Resource and retrieve the sharing handle.
IDXGISurface* dxgiSurface;
IDXGIResource* dxgiResource;
HANDLE shareHandle;
texture->QueryInterface<IDXGISurface>(&dxgiSurface);
dxgiSurface->QueryInterface<IDXGIResource>(&dxgiResource);
dxgiResource->GetSharedHandle(&shareHandle);
SafeRelease(&dxgiResource);
SafeRelease(&dxgiSurface);
//Call D3D 11 to open shared resource.
ID3D11Resource* d3d11Resource;
d3dDevice_->OpenSharedResource(shareHandle, __uuidof(ID3D11Resource), (void**)&d3d11Resource);
d3d11Resource->QueryInterface<ID3D11Texture2D>(&d3d11Texture);
SafeRelease(&d3d11Resource);
if (d3d11Texture)
{
d3d11Texture->Release();
}
CreateD2D1Resources(texture);
}
#pragma endregion
#pragma region CreateD2D1Resources
inline void GraphicsDevice::CreateD2D1Resources(ID3D10Texture2D* d3d10Texture)
{
pin_ptr<ID2D1Factory*> d2dFactoryPtr = &d2dFactory_;
pin_ptr<IDWriteFactory*> dWriteFactoryPtr = &dWriteFactory_;
//pin_ptr<ID2D1HwndRenderTarget*> d2dRenderTargetPtr = &d2dRenderTarget_;
//pin_ptr<ID2D1SolidColorBrush*> D2DSolidColorBrushPtr = &d2dSolidColorBrush_;
GraphicsDeviceNativeWrapper::CreateFactories(Window, d2dFactoryPtr, dWriteFactoryPtr);
//Get DXGI Surface from the created render target.
IDXGISurface1* pRT10;
d3d10Texture->QueryInterface<IDXGISurface1>(&pRT10);
FLOAT dpiX;
FLOAT dpiY;
d2dFactory_->GetDesktopDpi(&dpiX, &dpiY);
// Create a DC render target.
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_IGNORE),
static_cast<float>(dpiX),
static_cast<float>(dpiY)
);
// Create a Direct2D render target.
// Assuming m_pD2DFactory was previously created with:
//D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), NULL,
// (void**)(&m_pD2DFactory));
pin_ptr<ID2D1RenderTarget*> renderTargetPtr = &d2dRenderTarget_;
d2dFactory_->CreateDxgiSurfaceRenderTarget(pRT10, (const D2D1_RENDER_TARGET_PROPERTIES *)&props, renderTargetPtr);
pin_ptr<ID2D1SolidColorBrush*> solidColorBrushPtr = &d2dSolidColorBrush_;
d2dRenderTarget_->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), solidColorBrushPtr);
}
#pragma endregion
// File GraphicsDeviceNativeWrapper.h
#pragma once
#ifndef _GRAPHICSDEVICENATIVEWRAPPER_H_
#define _GRAPHICSDEVICENATIVEWRAPPER_H_
#ifndef _DWRITE_H_
#include "DWrite.h"
#endif
#pragma managed(push, false)
namespace BSGameFramework
{
namespace Graphics
{
class GraphicsDeviceNativeWrapper abstract sealed
{
public:
static void CreateFactories(HWND window, ID2D1Factory** d2dFactory, IDWriteFactory** dWriteFactory/*,ID2D1RenderTarget** d2dRenderTarget, ID2D1SolidColorBrush** d2dSolidColorBrush*/)
{
HRESULT result;
result = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,__uuidof(ID2D1Factory), NULL, (void**)d2dFactory);
if (SUCCEEDED(result))
{
result = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(dWriteFactory));
}
RECT rc;
GetClientRect(window, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
}
static void CreateTextFormat(const wchar_t* font, IDWriteFactory* factory, IDWriteTextFormat** format)
{
factory->CreateTextFormat(font, NULL, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 72.0f, L"en-us", format);
(*format)->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
(*format)->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
}
};
}
}
#pragma managed(pop)
#endif
And this is my SpriteBatch:
// File: SpriteBatch.h
#pragma once
#ifndef _SPRITEBATCH_H_
#define _SPRITEBATCH_H_
#ifndef _DEFINES_H_
#include "Defines.h"
#endif
#ifndef _GRAPHICRESOURCE_H_
#include "GraphicResource.h"
#endif
#ifndef _TEXTURE2D_H_
#include "Texture2D.h"
#endif
#ifndef _GRAPHICSDEVICE_H_
#include "GraphicsDevice.h"
#endif
#ifndef _SPRITESORTMODE_H_
#include "SpriteSortMode.h"
#endif
#ifndef _BLENDSTATE_H_
#include "BlendState.h"
#endif
#ifndef _NATIVESPRITEBATCH_H_
#include "NativeSpritebatch.h"
#endif
#ifndef _SPRITEEFFECT_H_
#include "SpriteEffect.h"
#endif
#ifndef _IDRAWABLECOMPONENT_H_
#include "IDrawableComponent.h"
#endif
#ifndef _SPRITEFONT_H_
#include "SpriteFont.h"
#endif
using namespace BSGameFramework::GameBase;
namespace BSGameFramework
{
namespace Graphics
{
public ref class SpriteBatch : GraphicResource
{
public:
SpriteBatch(GraphicsDevice^ graphicsDevice);
~SpriteBatch();
void Begin();
void Begin(SpriteSortMode sortMode, BlendState^ blendState);
void Draw(IDrawableComponent^ component);
void DrawString(SpriteFont^ font, System::String^ text, Vector2 position);
void End();
private:
bool CompileD3DShader(char* filePath, char* entry, char* shaderModel, ID3DBlob** buffer);
void SortByDepth();
SpriteSortMode sortMode_;
BlendState ^blendState_;
System::Collections::Generic::List<IDrawableComponent^>^ componentList_;
bool beginInvoked_;
ID3D11VertexShader* solidColorVS_;
ID3D11PixelShader* solidColorPS_;
ID3D11InputLayout* inputLayout_;
ID3D11Buffer* vertexBuffer_;
ID3D11BlendState* alphaBlendState_;
NativeSpritebatch* spriteBatch;
};
}
}
#endif
// File: SpriteBatch.cpp
#include "SpriteBatch.h"
#ifndef _SPRITEBATCHBEGINENDEXCEPTION_H_
#include "SpriteBatchBeginEndException.h"
#endif
using namespace BSGameFramework::Graphics;
using namespace BSGameFramework::Exception;
inline SpriteBatch::SpriteBatch(GraphicsDevice^ graphicsDevice) : alphaBlendState_( 0 )
{
graphicDevice_ = graphicsDevice;
sortMode_ = SpriteSortMode::Deferred;
blendState_ = BlendState::AlphaBlend;
// ID3DBlob contiene un puntatore ad un dato di lunghezza qualsiasi, GetBufferPointer restituisce il puntatore e GetBufferSize la grandezza
ID3DBlob* vsBuffer = 0;
// Compila lo shader e salva il risultato nel buffer
bool compileResult = CompileD3DShader("TextureMap.fx", "VS_Main", "vs_4_0", &vsBuffer);
if (compileResult == false)
{
DXTRACE_MSG("Error compiling the vertex shader!");
return;
}
HRESULT d3dResult;
pin_ptr<ID3D11VertexShader*> solidColorVSPointer;
solidColorVSPointer = &solidColorVS_;
// Crea il vertex shader e lo salva in solidColorVS_ di tipo ID3D11VertexShader*
d3dResult = Device->D3DDevice->CreateVertexShader(vsBuffer->GetBufferPointer(), vsBuffer->GetBufferSize(), 0, solidColorVSPointer);
if (FAILED(d3dResult))
{
DXTRACE_MSG("Error creating the vertex shader!");
if (vsBuffer)
{
vsBuffer->Release();
}
return;
}
D3D11_INPUT_ELEMENT_DESC solidColorLayout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
unsigned int totalLayoutElements = ARRAYSIZE(solidColorLayout);
pin_ptr<ID3D11InputLayout*> inputLayoutPointer;
inputLayoutPointer = &inputLayout_;
// Crea l'input layout e lo salva in inputLayout di tipo ID3D11InputLayout*
d3dResult = Device->D3DDevice->CreateInputLayout(solidColorLayout, totalLayoutElements, vsBuffer->GetBufferPointer(), vsBuffer->GetBufferSize(), inputLayoutPointer);
vsBuffer->Release();
if (FAILED(d3dResult))
{
DXTRACE_MSG("Error creating the input layout!");
return;
}
ID3DBlob* psBuffer = 0;
// Compila il pixel shader e salva il risultato in psBuffer
compileResult = CompileD3DShader("TextureMap.fx", "PS_Main", "ps_4_0", &psBuffer);
if (compileResult == false)
{
DXTRACE_MSG("Error compiling pixel shader!");
return;
}
pin_ptr<ID3D11PixelShader*> solidColorPSPointer;
solidColorPSPointer = &solidColorPS_;
// Crea il pixel shader e lo salva in solidColorPS_ di tipo ID3D11PixelShader*
d3dResult = Device->D3DDevice->CreatePixelShader(psBuffer->GetBufferPointer(), psBuffer->GetBufferSize(), 0, solidColorPSPointer);;
psBuffer->Release();
if (FAILED(d3dResult))
{
DXTRACE_MSG("Error creating pixel shader!");
return;
}
spriteBatch = new NativeSpritebatch(Device->D3DDevice);
// Spostare nel Begin successivamente
D3D11_BLEND_DESC blendDesc;
ZeroMemory(&blendDesc, sizeof(D3D11_BLEND_DESC));
blendDesc.AlphaToCoverageEnable = FALSE;
blendDesc.IndependentBlendEnable = FALSE;
blendDesc.RenderTarget[0].BlendEnable = TRUE;
blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_DEST_ALPHA;
blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
float blendFactor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
pin_ptr<ID3D11BlendState*> alphaBlendStatePointer;
alphaBlendStatePointer = &alphaBlendState_;
Device->D3DDevice->CreateBlendState(&blendDesc, alphaBlendStatePointer);
Device->D3DContext->OMSetBlendState(alphaBlendState_, blendFactor, 0xFFFFFFFF);
}
inline SpriteBatch::~SpriteBatch()
{
}
inline void SpriteBatch::Begin()
{
if (beginInvoked_)
{
throw gcnew SpriteBatchBeginEndException("Beetween two SpriteBatch begin methods you must call SpriteBacth End method!");
}
beginInvoked_ = true;
Device->D2DRenderTarget->BeginDraw();
Device->D2DRenderTarget->SetTransform(D2D1::IdentityMatrix());
if (componentList_ == nullptr)
{
componentList_ = gcnew System::Collections::Generic::List<IDrawableComponent^>();
}
}
inline void SpriteBatch::Begin(SpriteSortMode sortMode, BlendState^ blendState)
{
Begin();
sortMode_ = sortMode;
}
inline void SpriteBatch::Draw(IDrawableComponent^ component)
{
if (component == nullptr)
{
throw gcnew ArgumentNullException("Component argument is null, please ensure to initialize all components correctly!");
}
else
{
componentList_->Add(component);
}
}
inline void SpriteBatch::DrawString(SpriteFont^ font, System::String^ text, Vector2 position)
{
RECT rc;
GetClientRect(Device->Window, &rc);
// Create a D2D rect that is the same size as the window.
D2D1_RECT_F layoutRect = D2D1::RectF(
static_cast<FLOAT>(rc.left) / font->DpiScaleX,
static_cast<FLOAT>(rc.top) / font->DpiScaleY,
static_cast<FLOAT>(rc.right - rc.left) / font->DpiScaleX,
static_cast<FLOAT>(rc.bottom - rc.top) / font->DpiScaleY
);
// Use the DrawText method of the D2D render target interface to draw.
WCHAR textUnicode = Utilities::StringToWCHAR(text);
UINT32 cTextLength_ = (UINT32) wcslen(&textUnicode);
Device->D2DSolidColorBrush->SetColor(D2D1::ColorF(0,0,0,1));
Device->D2DSolidColorBrush->SetColor(D2D1::ColorF(255, 255, 255, 255));
Device->D2DRenderTarget->DrawText(&textUnicode, cTextLength_, font->DWriteTextFormat, layoutRect, Device->D2DSolidColorBrush);
}
inline void SpriteBatch::End()
{
if (componentList_->Count)
{
if (sortMode_ == SpriteSortMode::BackToFront)
{
SortByDepth();
}
for (int i = 0; i < componentList_->Count; i++)
{
Texture* text = componentList_[i]->Texture->TextureInfo;
unsigned int stride = sizeof(VertexPos);
unsigned int offset = 0;
Device->D3DContext->IASetInputLayout(inputLayout_);
if (componentList_[i]->Effect != SpriteEffect::None)
{
ID3D11Buffer* vertexBuffer;
float width = (float)text->textureDesc_.Width;
float height = (float)text->textureDesc_.Height;
D3D11_BUFFER_DESC vertexDesc;
ZeroMemory(&vertexDesc, sizeof(vertexDesc));
vertexDesc.Usage = D3D11_USAGE_DYNAMIC;
vertexDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
vertexDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexDesc.ByteWidth = sizeof(VertexPos) * 6;
D3D11_SUBRESOURCE_DATA resourceData;
ZeroMemory(&resourceData, sizeof(resourceData));
pin_ptr<ID3D11Buffer*> vertexBufferPointer;
vertexBufferPointer = &vertexBuffer;
switch (componentList_[i]->Effect)
{
case BSGameFramework::Graphics::SpriteEffect::FlipHorizontally:
{
VertexPos verticesOne[] =
{
{ XMFLOAT3(width, height, 1.0f), XMFLOAT2(0.0f, 0.0f) },
{ XMFLOAT3(width, 0.0f, 1.0f), XMFLOAT2(0.0f, 1.0f) },
{ XMFLOAT3(0.0f, 0.0f, 1.0f), XMFLOAT2(1.0f, 1.0f) },
{ XMFLOAT3(0.0f, 0.0f, 1.0f), XMFLOAT2(1.0f, 1.0f) },
{ XMFLOAT3(0.0f, height, 1.0f), XMFLOAT2(1.0f, 0.0f) },
{ XMFLOAT3(width, height, 1.0f), XMFLOAT2(0.0f, 0.0f) },
};
resourceData.pSysMem = verticesOne;
Device->D3DDevice->CreateBuffer(&vertexDesc, &resourceData, vertexBufferPointer);
Device->D3DContext->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);
break;
}
case BSGameFramework::Graphics::SpriteEffect::FlipVertically:
{
VertexPos verticesTwo[] =
{
{ XMFLOAT3(width, height, 1.0f), XMFLOAT2(1.0f, 1.0f) },
{ XMFLOAT3(width, 0.0f, 1.0f), XMFLOAT2(1.0f, 0.0f) },
{ XMFLOAT3(0.0f, 0.0f, 1.0f), XMFLOAT2(0.0f, 0.0f) },
{ XMFLOAT3(0.0f, 0.0f, 1.0f), XMFLOAT2(0.0f, 0.0f) },
{ XMFLOAT3(0.0f, height, 1.0f), XMFLOAT2(0.0f, 1.0f) },
{ XMFLOAT3(width, height, 1.0f), XMFLOAT2(1.0f, 1.0f) },
};
resourceData.pSysMem = verticesTwo;
Device->D3DDevice->CreateBuffer(&vertexDesc, &resourceData, vertexBufferPointer);
Device->D3DContext->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);
break;
}
}
}
else
{
Device->D3DContext->IASetVertexBuffers(0, 1, &text->vertexBuffer_, &stride, &offset);
}
Device->D3DContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
Device->D3DContext->VSSetShader(solidColorVS_, 0, 0);
Device->D3DContext->PSSetShader(solidColorPS_, 0, 0);
Device->D3DContext->PSSetShaderResources(0, 1, &text->colorMap_);
Device->D3DContext->PSSetSamplers(0, 1, &text->colorMapSampler_);
spriteBatch->SetTranspose(Device->D3DContext, text, Device->WindowWidth, Device->WindowHeight, componentList_[i]->Position.X, componentList_[i]->Position.Y,
componentList_[i]->Scale.X, componentList_[i]->Scale.Y, componentList_[i]->Rotation);
Device->D3DContext->Draw(6, 0);
}
}
Device->D2DRenderTarget->EndDraw();
componentList_->Clear();
beginInvoked_ = false;
sortMode_ = SpriteSortMode::Deferred;
}
inline bool SpriteBatch::CompileD3DShader(char* filePath, char* entry, char* shaderModel, ID3DBlob** buffer)
{
DWORD shaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined(DEBUG) || defined(_DEBUG)
shaderFlags |= D3DCOMPILE_DEBUG;
#endif
ID3DBlob* errorBuffer = 0;
HRESULT result;
result = D3DX11CompileFromFile(filePath, 0, 0, entry, shaderModel, shaderFlags, 0, 0, buffer, &errorBuffer, 0);
if (FAILED(result))
{
if (errorBuffer != 0)
{
OutputDebugStringA((char*)errorBuffer->GetBufferPointer());
errorBuffer->Release();
}
return false;
}
if (errorBuffer != 0)
{
errorBuffer->Release();
}
return true;
}
inline void SpriteBatch::SortByDepth()
{
for (int i = 0; i < componentList_->Count - 1; i++)
{
for (int j = 1; j < componentList_->Count; j++)
{
if (componentList_[i]->ZIndex < componentList_[j]->ZIndex)
{
IDrawableComponent^ component = componentList_[i];
componentList_[i] = componentList_[j];
componentList_[j] = component;
}
}
}
}
When I call SpriteBatch.DrawString(...) from my C# app I'm not obtaining nothing written on my screen, can please somebody explaining me what's I'm missing? I'm new on DirectX programming so please be quiet with me xD Thanks!
There's a lot of code you posted and I didn't read it all, but I was trying to do the same thing (successfully) a few days ago and I might have some suggestions.
Overall, it looks like you do this more or less the same way I do. What I did different it that I created the shared texture using D3D11 device, but this should make no difference. The second thing, which I think could be the problem, is that you're not synchronizing the texture resource between D3D10 and D3D11.
When I tried to use such resources without synchronization, D2D was only rendering stuff once in like 50 calls, the rest just had no effect. I think I was continously blocking the resource when rendering it with D3D11.
You should create the texture using
tDesc.MiscFlags = D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX;
instead. Here are the details about this flag: MSDN. Basically, you query two IDXGIKeyedMutex interfaces from the shared texture objects on both D3D10 and D3D11 devices. You lock the D3D10 mutex when you draw your D2D stuff to the texture. You lock the D3D11 mutex when you want to use this texture in D3D11. You do this using IDXGIKeyedMutex::AcquireSync and IDXGIKeyedMutex::ReleaseSync. Just be sure to pass the same Key value to each consecutive D3D10 Release -> D3D11 Acquire and D3D11 Release -> D3D10 Acquire calls.
Oh, one more little, not important thing, I noticed this in your code:
texture->QueryInterface<IDXGISurface>(&dxgiSurface);
dxgiSurface->QueryInterface<IDXGIResource>(&dxgiResource);
I'm not 100% sure, but I think you can get the IDXGIResource interface directly from your texture.