I've been struggling with a resource leak seemingly caused by NVIDIA's h.264 encoder MFT. Each time a frame is submitted to the encoder, the reference count of my D3D device is incremented by 1, and this reference is not given up even after shutting down the MFT. A bunch of threads are leaked as well.
I'm almost ready to bring this up with NVIDIA, but I'd like to first make sure there's nothing obvious I have missed. Please see my implementation below - I've tried to keep it as concise and clear as possible.
Arguments for why this might be a problem with NVIDIA's encoder:
This only happens with NVIDIA's encoder. No leak is observed when running on e.g. Intel's QuickSync.
Arguments for why this might be a problem in my code:
I've tried using a SinkWriter to write DXGI surfaces to a file in a similar fashion, and here the leak is not present. Unfortunately I don't have access to the source code of SinkWriter. I would be very happy if anyone could point me to some working sample code that I could compare against.
#pragma comment(lib, "D3D11.lib")
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mf.lib")
#pragma comment(lib, "evr.lib")
#pragma comment(lib, "mfuuid.lib")
#pragma comment(lib, "Winmm.lib")
// std
#include <iostream>
#include <string>
// Windows
#include <windows.h>
#include <atlbase.h>
// DirectX
#include <d3d11.h>
// Media Foundation
#include <mfapi.h>
#include <mfplay.h>
#include <mfreadwrite.h>
#include <mferror.h>
#include <Codecapi.h>
// Error handling
#define CHECK(x) if (!(x)) { printf("%s(%d) %s was false\n", __FILE__, __LINE__, #x); throw std::exception(); }
#define CHECK_HR(x) { HRESULT hr_ = (x); if (FAILED(hr_)) { printf("%s(%d) %s failed with 0x%x\n", __FILE__, __LINE__, #x, hr_); throw std::exception(); } }
// Constants
constexpr UINT ENCODE_WIDTH = 1920;
constexpr UINT ENCODE_HEIGHT = 1080;
constexpr UINT ENCODE_FRAMES = 120;
void runEncode();
int main()
{
CHECK_HR(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
CHECK_HR(MFStartup(MF_VERSION));
for (;;)
{
runEncode();
if (getchar() == 'q')
break;
}
CHECK_HR(MFShutdown());
return 0;
}
void runEncode()
{
CComPtr<ID3D11Device> device;
CComPtr<ID3D11DeviceContext> context;
CComPtr<IMFDXGIDeviceManager> deviceManager;
CComPtr<IMFVideoSampleAllocatorEx> allocator;
CComPtr<IMFTransform> transform;
CComPtr<IMFAttributes> transformAttrs;
CComQIPtr<IMFMediaEventGenerator> eventGen;
DWORD inputStreamID;
DWORD outputStreamID;
// ------------------------------------------------------------------------
// Initialize D3D11
// ------------------------------------------------------------------------
CHECK_HR(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_VIDEO_SUPPORT | D3D11_CREATE_DEVICE_DEBUG, NULL, 0, D3D11_SDK_VERSION, &device, NULL, &context));
{
// Probably not necessary in this application, but maybe the MFT requires it?
CComQIPtr<ID3D10Multithread> mt(device);
CHECK(mt);
mt->SetMultithreadProtected(TRUE);
}
// Create device manager
UINT resetToken;
CHECK_HR(MFCreateDXGIDeviceManager(&resetToken, &deviceManager));
CHECK_HR(deviceManager->ResetDevice(device, resetToken));
// ------------------------------------------------------------------------
// Initialize hardware encoder MFT
// ------------------------------------------------------------------------
{
// Find the encoder
CComHeapPtr<IMFActivate*> activateRaw;
UINT32 activateCount = 0;
// Input & output types
MFT_REGISTER_TYPE_INFO inInfo = { MFMediaType_Video, MFVideoFormat_NV12 };
MFT_REGISTER_TYPE_INFO outInfo = { MFMediaType_Video, MFVideoFormat_H264 };
// Query for the adapter LUID to get a matching encoder for the device.
CComQIPtr<IDXGIDevice> dxgiDevice(device);
CHECK(dxgiDevice);
CComPtr<IDXGIAdapter> adapter;
CHECK_HR(dxgiDevice->GetAdapter(&adapter));
DXGI_ADAPTER_DESC adapterDesc;
CHECK_HR(adapter->GetDesc(&adapterDesc));
CComPtr<IMFAttributes> enumAttrs;
CHECK_HR(MFCreateAttributes(&enumAttrs, 1));
CHECK_HR(enumAttrs->SetBlob(MFT_ENUM_ADAPTER_LUID, (BYTE*)&adapterDesc.AdapterLuid, sizeof(LUID)));
CHECK_HR(MFTEnum2(MFT_CATEGORY_VIDEO_ENCODER, MFT_ENUM_FLAG_HARDWARE | MFT_ENUM_FLAG_SORTANDFILTER, &inInfo, &outInfo, enumAttrs, &activateRaw, &activateCount));
CHECK(activateCount != 0);
// Choose the first returned encoder
CComPtr<IMFActivate> activate = activateRaw[0];
// Memory management
for (UINT32 i = 0; i < activateCount; i++)
activateRaw[i]->Release();
// Activate
CHECK_HR(activate->ActivateObject(IID_PPV_ARGS(&transform)));
// Get attributes
CHECK_HR(transform->GetAttributes(&transformAttrs));
}
// ------------------------------------------------------------------------
// Query encoder name (not necessary, but nice) and unlock for async use
// ------------------------------------------------------------------------
{
UINT32 nameLength = 0;
std::wstring name;
CHECK_HR(transformAttrs->GetStringLength(MFT_FRIENDLY_NAME_Attribute, &nameLength));
// IMFAttributes::GetString returns a null-terminated wide string
name.resize((size_t)nameLength + 1);
CHECK_HR(transformAttrs->GetString(MFT_FRIENDLY_NAME_Attribute, &name[0], (UINT32)name.size(), &nameLength));
name.resize(nameLength);
printf("Using %ls\n", name.c_str());
// Unlock the transform for async use and get event generator
CHECK_HR(transformAttrs->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE));
CHECK(eventGen = transform);
}
// Get stream IDs (expect 1 input and 1 output stream)
{
HRESULT hr = transform->GetStreamIDs(1, &inputStreamID, 1, &outputStreamID);
if (hr == E_NOTIMPL)
{
inputStreamID = 0;
outputStreamID = 0;
hr = S_OK;
}
CHECK_HR(hr);
}
// ------------------------------------------------------------------------
// Configure hardware encoder MFT
// ------------------------------------------------------------------------
// Set D3D manager
CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, reinterpret_cast<ULONG_PTR>(deviceManager.p)));
// Set output type
CComPtr<IMFMediaType> outputType;
CHECK_HR(MFCreateMediaType(&outputType));
CHECK_HR(outputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
CHECK_HR(outputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264));
CHECK_HR(outputType->SetUINT32(MF_MT_AVG_BITRATE, 30000000));
CHECK_HR(MFSetAttributeSize(outputType, MF_MT_FRAME_SIZE, ENCODE_WIDTH, ENCODE_HEIGHT));
CHECK_HR(MFSetAttributeRatio(outputType, MF_MT_FRAME_RATE, 60, 1));
CHECK_HR(outputType->SetUINT32(MF_MT_INTERLACE_MODE, 2));
CHECK_HR(outputType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE));
CHECK_HR(transform->SetOutputType(outputStreamID, outputType, 0));
// Set input type
CComPtr<IMFMediaType> inputType;
CHECK_HR(transform->GetInputAvailableType(inputStreamID, 0, &inputType));
CHECK_HR(inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
CHECK_HR(inputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12));
CHECK_HR(MFSetAttributeSize(inputType, MF_MT_FRAME_SIZE, ENCODE_WIDTH, ENCODE_HEIGHT));
CHECK_HR(MFSetAttributeRatio(inputType, MF_MT_FRAME_RATE, 60, 1));
CHECK_HR(transform->SetInputType(inputStreamID, inputType, 0));
// ------------------------------------------------------------------------
// Create sample allocator
// ------------------------------------------------------------------------
{
MFCreateVideoSampleAllocatorEx(IID_PPV_ARGS(&allocator));
CHECK(allocator);
CComPtr<IMFAttributes> allocAttrs;
MFCreateAttributes(&allocAttrs, 2);
CHECK_HR(allocAttrs->SetUINT32(MF_SA_D3D11_BINDFLAGS, D3D11_BIND_RENDER_TARGET));
CHECK_HR(allocAttrs->SetUINT32(MF_SA_D3D11_USAGE, D3D11_USAGE_DEFAULT));
CHECK_HR(allocator->SetDirectXManager(deviceManager));
CHECK_HR(allocator->InitializeSampleAllocatorEx(1, 2, allocAttrs, inputType));
}
// ------------------------------------------------------------------------
// Start encoding
// ------------------------------------------------------------------------
CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL));
CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL));
CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL));
// Encode loop
for (int i = 0; i < ENCODE_FRAMES; i++)
{
// Get next event
CComPtr<IMFMediaEvent> event;
CHECK_HR(eventGen->GetEvent(0, &event));
MediaEventType eventType;
CHECK_HR(event->GetType(&eventType));
switch (eventType)
{
case METransformNeedInput:
{
CComPtr<IMFSample> sample;
CHECK_HR(allocator->AllocateSample(&sample));
CHECK_HR(transform->ProcessInput(inputStreamID, sample, 0));
// Dereferencing the device once after feeding each frame "fixes" the leak.
//device.p->Release();
break;
}
case METransformHaveOutput:
{
DWORD status;
MFT_OUTPUT_DATA_BUFFER outputBuffer = {};
outputBuffer.dwStreamID = outputStreamID;
CHECK_HR(transform->ProcessOutput(0, 1, &outputBuffer, &status));
DWORD bufCount;
DWORD bufLength;
CHECK_HR(outputBuffer.pSample->GetBufferCount(&bufCount));
CComPtr<IMFMediaBuffer> outBuffer;
CHECK_HR(outputBuffer.pSample->GetBufferByIndex(0, &outBuffer));
CHECK_HR(outBuffer->GetCurrentLength(&bufLength));
printf("METransformHaveOutput buffers=%d, bytes=%d\n", bufCount, bufLength);
// Release the sample as it is not processed further.
if (outputBuffer.pSample)
outputBuffer.pSample->Release();
if (outputBuffer.pEvents)
outputBuffer.pEvents->Release();
break;
}
}
}
// ------------------------------------------------------------------------
// Finish encoding
// ------------------------------------------------------------------------
CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, NULL));
CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, NULL));
CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, NULL));
// Shutdown
printf("Finished encoding\n");
// I've tried all kinds of things...
//CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, reinterpret_cast<ULONG_PTR>(nullptr)));
//transform->SetInputType(inputStreamID, NULL, 0);
//transform->SetOutputType(outputStreamID, NULL, 0);
//transform->DeleteInputStream(inputStreamID);
//deviceManager->ResetDevice(NULL, resetToken);
CHECK_HR(MFShutdownObject(transform));
}
I think the answer is “yes”.
I saw the problem before: Is it possible to shut down a D3D device?
To workaround, I stopped re-creating D3D devices. Instead I’m using a global CAtlMap collection. The keys are uint64_t containing LUID of the GPU from DXGI_ADAPTER_DESC::AdapterLuid field. The values are structures with 2 fields, CComPtr<ID3D11Device> and CComPtr<IMFDXGIDeviceManager>
Related
im learning directx im working on initializing direct3d in my application and all of my HRESULTS are returning S_OK which is a success return code except the last one which involved swap chain creation i have the debug layer enabled so im getting a warning error in the Output Log that says 'OurDevice' could be 0... (OurDevice is the pointer name to my ID3D11Device).. after many hours of scanning through Microsoft Docs and checking and rechecking all my code i cant seem to find out the issue does anyone have a clue ?
#include "dx3dmanager.h"
#include "Core.h"
#include <Windows.h>
#include <d3d11.h>
#include <d3d11_1.h>
#include <d3d11_2.h>
#include <DirectXMath.h>
#include <dxgi.h>
dx3dmanager::dx3dmanager()
{
}
dx3dmanager::~dx3dmanager()
{
}
// this method intitializes the Direct3d version 11 API this
initialization method is subject to change with new versions /*
void dx3dmanager::initialize3d(HWND MainWindow)
{
// declarations for Direct3D feature levels ours will include
versions 11-12 unless changed /*
//declarations for our device(GPU) and device context /*
UINT creationflags = D3D11_CREATE_DEVICE_DEBUG;
ID3D11Device* OurDevice;
ID3D11DeviceContext* IDeviceContext;
D3D_FEATURE_LEVEL featurelevels[2] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_11_1
};
HRESULT CreateDev;
switch (CreateDev) {
case S_OK: {
MessageBeep(0xFFFFFFFF);
}
}
// checks multisampling quality
HRESULT checkmxquality;
UINT m4xMsaaQuality;
DXGI_FORMAT dxgi_format = DXGI_FORMAT_R8G8B8A8_UNORM;
checkmxquality = OurDevice->CheckMultisampleQualityLevels(dxgi_format, 4, &m4xMsaaQuality);
assert(m4xMsaaQuality > 0);
switch (checkmxquality) {
case S_OK: {
MessageBeep(0xFFFFFFFF);
}
break;
}
// descriptions for rendering structs IE: sampling, format, and swap
chain /*
DXGI_SAMPLE_DESC sampdesc;
sampdesc.Count = 1;
sampdesc.Quality = 0;
DXGI_MODE_DESC1 dxmode;
dxmode.Width = 125;
dxmode.Height = 125;
dxmode.Format = dxgi_format;
dxmode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
dxmode.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
dxmode.RefreshRate.Numerator = 60;
dxmode.RefreshRate.Denominator = 1;
dxmode.Stereo = false;
DXGI_SWAP_CHAIN_DESC1 chaindesc;
chaindesc.Width = 0;
chaindesc.Height = 0;
chaindesc.Format = dxgi_format;
chaindesc.Stereo = false;
chaindesc.SampleDesc = sampdesc;
chaindesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
chaindesc.BufferCount = 3;
chaindesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
chaindesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH|
DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE| DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY;
chaindesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
chaindesc.Scaling = DXGI_SCALING_STRETCH;
//-------------------------------------------------------------------
-------------------------------/*
// this is our DXGI interface (DXGI is not a part of direct3d it is a
seperate API (COM) /*
IDXGIDevice* dxgiDevice = nullptr;
CreateDev = OurDevice->QueryInterface(__uuidof(IDXGIDevice),
(void**)& dxgiDevice);
if (SUCCEEDED(CreateDev)) {
MessageBeep(0xFFFFFFFF);
}
IDXGIAdapter* dxgiadapter = nullptr;
CreateDev = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&
dxgiadapter);
if (SUCCEEDED(CreateDev)) {
MessageBeep(0xFFFFFFFF);
}
IDXGIFactory2* factory;
CreateDev = dxgiadapter->GetParent(__uuidof(IDXGIFactory), (void**)&
factory);
if (SUCCEEDED(CreateDev)) {
MessageBeep(0xFFFFFFFF);
}
IDXGISwapChain1* mSwapChain;
CreateDev = factory->CreateSwapChainForHwnd(OurDevice, MainWindow,
&chaindesc, NULL, NULL, &mSwapChain);
if (FAILED(CreateDev)) {
MessageBeep(0xFFFFFFFF);
}
//-------------------------------------------------------------------
-------------------------------/*
If you enable DXGI debugging as well as using D3D11_CREATE_DEVICE_DEBUG, then you will get output messages in the debug window that tells you problems that result in failed error codes.
Even with just the Direct3D 11 debugging enabled, you get:
D3D11 ERROR: ID3D11Device::CreateTexture2D1: D3D11_RESOURCE_MISC_GDI_COMPATIBLE
requires a B8G8R8A8 format.
[ STATE_CREATION ERROR #103: CREATETEXTURE2D_INVALIDMISCFLAGS]
So your problem is very simple and easy to fix: Change dxgi_format to DXGI_FORMAT_B8G8R8A8_UNORM
See Anatomy of Direct3D 11 Create Device as well as Direct3D Game Visual Studio templates
I'm using OpenAL-Soft for a project, and right now I'm trying to decide whether I need to implement OpenAL source pooling.
Source pooling is somewhat cumbersome (I need to write code to "allocate" sources, as well as somehow decide when they can be "freed"), but necessary if the number of sources that can be generated by OpenAL is limited.
Since OpenAL-Soft is a software implementation of the OpenAL API, I wonder if the number of sources it can generate is actually limited by the underlying hardware. Theoretically, since all mixing is done in software, there might be no need to actually use one hardware channel per source.
However, I'm not sure about it. How should I proceed?
It appears that OpenAL-Soft indeed does have an upper limit on the number of sources, which can be defined in a config file. The default seems to be 256. It makes sense to limit the number of sources because of the associated CPU and memory costs. Looks like I'll end up implementing a source pool after all.
I just took a peek at its header ... did not see anything pop out.
Here is working code which synthesizes then renders audio buffer data ... you could play with seeing if it accommodates your necessary number of sources
// gcc -o openal_play_wed openal_play_wed.c -lopenal -lm
#include <stdio.h>
#include <stdlib.h> // gives malloc
#include <math.h>
#ifdef __APPLE__
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#elif __linux
#include <AL/al.h>
#include <AL/alc.h>
#endif
ALCdevice * openal_output_device;
ALCcontext * openal_output_context;
ALuint internal_buffer;
ALuint streaming_source[1];
int al_check_error(const char * given_label) {
ALenum al_error;
al_error = alGetError();
if(AL_NO_ERROR != al_error) {
printf("ERROR - %s (%s)\n", alGetString(al_error), given_label);
return al_error;
}
return 0;
}
void MM_init_al() {
const char * defname = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
openal_output_device = alcOpenDevice(defname);
openal_output_context = alcCreateContext(openal_output_device, NULL);
alcMakeContextCurrent(openal_output_context);
// setup buffer and source
alGenBuffers(1, & internal_buffer);
al_check_error("failed call to alGenBuffers");
}
void MM_exit_al() {
ALenum errorCode = 0;
// Stop the sources
alSourceStopv(1, & streaming_source[0]); // streaming_source
int ii;
for (ii = 0; ii < 1; ++ii) {
alSourcei(streaming_source[ii], AL_BUFFER, 0);
}
// Clean-up
alDeleteSources(1, &streaming_source[0]);
alDeleteBuffers(16, &streaming_source[0]);
errorCode = alGetError();
alcMakeContextCurrent(NULL);
errorCode = alGetError();
alcDestroyContext(openal_output_context);
alcCloseDevice(openal_output_device);
}
void MM_render_one_buffer() {
/* Fill buffer with Sine-Wave */
// float freq = 440.f;
float freq = 100.f;
float incr_freq = 0.1f;
int seconds = 4;
// unsigned sample_rate = 22050;
unsigned sample_rate = 44100;
double my_pi = 3.14159;
size_t buf_size = seconds * sample_rate;
short * samples = malloc(sizeof(short) * buf_size);
printf("\nhere is freq %f\n", freq);
int i=0;
for(; i<buf_size; ++i) {
samples[i] = 32760 * sin( (2.f * my_pi * freq)/sample_rate * i );
freq += incr_freq;
// incr_freq += incr_freq;
// freq *= factor_freq;
if (100.0 > freq || freq > 5000.0) {
incr_freq *= -1.0f;
}
}
/* upload buffer to OpenAL */
alBufferData( internal_buffer, AL_FORMAT_MONO16, samples, buf_size, sample_rate);
al_check_error("populating alBufferData");
free(samples);
/* Set-up sound source and play buffer */
// ALuint src = 0;
// alGenSources(1, &src);
// alSourcei(src, AL_BUFFER, internal_buffer);
alGenSources(1, & streaming_source[0]);
alSourcei(streaming_source[0], AL_BUFFER, internal_buffer);
// alSourcePlay(src);
alSourcePlay(streaming_source[0]);
// ---------------------
ALenum current_playing_state;
alGetSourcei(streaming_source[0], AL_SOURCE_STATE, & current_playing_state);
al_check_error("alGetSourcei AL_SOURCE_STATE");
while (AL_PLAYING == current_playing_state) {
printf("still playing ... so sleep\n");
sleep(1); // should use a thread sleep NOT sleep() for a more responsive finish
alGetSourcei(streaming_source[0], AL_SOURCE_STATE, & current_playing_state);
al_check_error("alGetSourcei AL_SOURCE_STATE");
}
printf("end of playing\n");
/* Dealloc OpenAL */
MM_exit_al();
} // MM_render_one_buffer
int main() {
MM_init_al();
MM_render_one_buffer();
}
I am trying to output a string from the PIC's USART and have it display on Tera Term. I am using the:
PIC18F4331
Sparkfun Bluesmirf RN-42
MPLAB v8.85
Tera Term
I've been working at this code for a couple of days and I am not seeing a single response. A couple of things that I think may be causing the issue is the baud rate and/or not having an interrupt routine. But is there a need for an interrupt if I am only attempting to transmit? Please, can someone guide me? Also, when using printf, I am seeing a response through the bluetooth but in strange symbolic form. For example, þþþ.
The code is a modification of one found online.
// Libraries
#include <p18f4331.h>
#include <stdio.h>
// Configuations
#pragma config OSC = XT
#pragma config WDTEN = OFF
#pragma config PWRTEN = OFF
#pragma config FCMEN = OFF
#pragma config IESO = OFF
#pragma config BOREN = ON
#pragma config BORV = 27
#pragma config WDPS = 128
#pragma config T1OSCMX = ON
#pragma config PWMPIN = ON
#pragma config MCLRE = ON
#pragma config LVP = OFF
#pragma config STVREN = OFF
#pragma config PWM4MX = RD5
// Definitions
#define _XTAL_FREQ 4000000
#define BAUDRATE 9600
void EUSART(void)
{
TRISC = 0b10000000;
SPBRG = 25;
TXSTAbits.CSRC = 0; // Baud Rate Generated Externally
TXSTAbits.TX9 = 0; // 8-Bit Transmission
TXSTAbits.TXEN = 1; // Transmit Enabled
TXSTAbits.SYNC = 0; // Asynchronous Mode
TXSTAbits.BRGH = 1; // High Baud Rate
TXSTAbits.TRMT = 0; // Transmit Shift Register When TSR Is Full
RCSTAbits.SPEN = 1; // Serial Port Enabled
RCSTAbits.RX9 = 0; // 8-Bit Reception
RCSTAbits.CREN = 1; // Enables Receive
}
void SendByteSerially(unsigned char Byte) // Writes a character to the serial port
{
while(!PIR1bits.TXIF) ; // wait for previous transmission to finish
TXREG = Byte;
}
unsigned char ReceiveByteSerially(void) // Reads a character from the serial port
{
while(!PIR1bits.RCIF) continue; // Wait for transmission to receive
return RCREG;
}
void SendStringSerially(const rom unsigned char* st)
{
while(*st) SendByteSerially(*st++);
}
void delayMS(unsigned int x)
{
unsigned char y;
for(;x > 0; x--) for(y=0; y< 82;y++);
}
void main(void)
{
unsigned char SerialData;
EUSART();
SendStringSerially("Hello World");
while(1)
{
SerialData = ReceiveByteSerially();
SendByteSerially(SerialData);
delayMS(1000);
}
}
You are using a PIC18, be sure that BRG16 equals 0 since you're using BRGH
BAUDCTL.BRG16 = 0;
because SPBRGH16 is the higher byte of SPBRG, and that may change the baudrate value of your USART.
Plus, be sure you're in the PIR1 bank. In MPLAB, that would be
banksel PIR1; //Not sure if there's an ending coma
My two functions to transmit via UART when properly initialized ( In MikroC ) :
void vTx232 (UC ucSend)
{
STATUS.RP0 = PIR1; //Sure we're in PIR1
while (PIR1.TXIF == 0);//While last TX not done
TXREG = ucSend; //Input param into TXREG
}
void vTxString(UC *ucpString)
{
while (*ucpString!= 0x00) //While string not at its end
{
vTx232(*ucpString); //Send string character
ucpString++; //Increm string pointer
}
}
My plan was to create a loading thread inside of which I load resources for a game; such as 3D models, shaders, textures, etc. On the main thread I perform all the game logic and rendering. Then, on my loading thread, I create a sf::Context (SFML shared OpenGL context) which is used only for loading.
This is working for loading shaders. However, xserver sometimes crashes when attempting to load models. I have narrowed the crash down to the glBufferData() call. I have checked that there is nothing wrong with the data that I am sending.
Is it possible call glBufferData() from a second thread using a second OpenGL context? If not, why is it possible to load shaders in the second context? If it is possible, what could be going wrong?
#include <iostream>
#include <thread>
#include <GL/glew.h>
#include <SFML/OpenGL.hpp>
#include <SFML/Graphics.hpp>
#include <X11/Xlib.h>
class ResourceLoader
{
public:
void Run()
{
sf::Context loadingContext;
loadingContext.setActive(true);
// Some test data.
float* testData = new float[3000];
for (unsigned int i = 0; i < 3000; ++i)
{
testData[i] = 0.0f;
}
// Create lots of VBOs containing our
// test data.
for (unsigned int i = 0; i < 1000; ++i)
{
// Create VBO.
GLuint testVBO = 0;
glGenBuffers(1, &testVBO);
std::cout << "Buffer ID: " << testVBO << std::endl;
// Bind VBO.
glBindBuffer(GL_ARRAY_BUFFER, testVBO);
// Crashes on this call!
glBufferData(
GL_ARRAY_BUFFER,
sizeof(float) * 3000,
&testData[0],
GL_STATIC_DRAW
);
// Unbind VBO.
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Sleep for a bit.
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
delete[] testData;
}
};
int main()
{
XInitThreads();
// Create the main window.
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML window", sf::Style::Default, sf::ContextSettings(32));
window.setVerticalSyncEnabled(true);
// Make it the active window for OpenGL calls.
window.setActive();
// Configure the OpenGL viewport to be the same size as the window.
glViewport(0, 0, window.getSize().x, window.getSize().y);
// Initialize GLEW.
glewExperimental = GL_TRUE; // OSX fix.
if (glewInit() != GLEW_OK)
{
window.close();
exit(1); // failure
}
// Enable Z-buffer reading and writing.
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
// Create the resource loader.
ResourceLoader loader;
// Run the resource loader in a separate thread.
std::thread loaderThread(&ResourceLoader::Run, &loader);
// Detach the loading thread, allowing it to run
// in the background.
loaderThread.detach();
// Game loop.
while (window.isOpen())
{
// Event loop.
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
window.close();
}
}
// Clear scren.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Switch to SFML's OpenGL state.
window.pushGLStates();
{
// Perform SFML drawing here...
sf::RectangleShape rect(sf::Vector2f(100.0f, 100.0f));
rect.setPosition(100.0f, 100.0f);
rect.setFillColor(sf::Color(255, 255, 255));
window.draw(rect);
}
// Switch back to our game rendering OpenGL state.
window.popGLStates();
// Perform OpenGL drawing here...
// Display the rendered frame.
window.display();
}
return 0;
}
I think the problem is that you call glBufferData before setting up GLEW, so the function pointer to glBufferData is not initialized. Please try this ordering for initializing your program:
Initialize the RenderWindow
Initialize GLEW
Start threads, and create additional contexts as required!
Can anyone shed light on the reason that when the below code is compiled and run on OSX the 'bartender' thread skips through the sem_wait() in what seems like a random manner and yet when compiled and run on a Linux machine the sem_wait() holds the thread until the relative call to sem_post() is made, as would be expected?
I am currently learning not only POSIX threads but concurrency as a whole so absoutely any comments, tips and insights are warmly welcomed...
Thanks in advance.
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
//using namespace std;
#define NSTUDENTS 30
#define MAX_SERVINGS 100
void* student(void* ptr);
void get_serving(int id);
void drink_and_think();
void* bartender(void* ptr);
void refill_barrel();
// This shared variable gives the number of servings currently in the barrel
int servings = 10;
// Define here your semaphores and any other shared data
sem_t *mutex_stu;
sem_t *mutex_bar;
int main() {
static const char *semname1 = "Semaphore1";
static const char *semname2 = "Semaphore2";
pthread_t tid;
mutex_stu = sem_open(semname1, O_CREAT, 0777, 0);
if (mutex_stu == SEM_FAILED)
{
fprintf(stderr, "%s\n", "ERROR creating semaphore semname1");
exit(EXIT_FAILURE);
}
mutex_bar = sem_open(semname2, O_CREAT, 0777, 1);
if (mutex_bar == SEM_FAILED)
{
fprintf(stderr, "%s\n", "ERROR creating semaphore semname2");
exit(EXIT_FAILURE);
}
pthread_create(&tid, NULL, bartender, &tid);
for(int i=0; i < NSTUDENTS; ++i) {
pthread_create(&tid, NULL, student, &tid);
}
pthread_join(tid, NULL);
sem_unlink(semname1);
sem_unlink(semname2);
printf("Exiting the program...\n");
}
//Called by a student process. Do not modify this.
void drink_and_think() {
// Sleep time in milliseconds
int st = rand() % 10;
sleep(st);
}
// Called by a student process. Do not modify this.
void get_serving(int id) {
if (servings > 0) {
servings -= 1;
} else {
servings = 0;
}
printf("ID %d got a serving. %d left\n", id, servings);
}
// Called by the bartender process.
void refill_barrel()
{
servings = 1 + rand() % 10;
printf("Barrel refilled up to -> %d\n", servings);
}
//-- Implement a synchronized version of the student
void* student(void* ptr) {
int id = *(int*)ptr;
printf("Started student %d\n", id);
while(1) {
sem_wait(mutex_stu);
if(servings > 0) {
get_serving(id);
} else {
sem_post(mutex_bar);
continue;
}
sem_post(mutex_stu);
drink_and_think();
}
return NULL;
}
//-- Implement a synchronized version of the bartender
void* bartender(void* ptr) {
int id = *(int*)ptr;
printf("Started bartender %d\n", id);
//sleep(5);
while(1) {
sem_wait(mutex_bar);
if(servings <= 0) {
refill_barrel();
} else {
printf("Bar skipped sem_wait()!\n");
}
sem_post(mutex_stu);
}
return NULL;
}
The first time you run the program, you're creating named semaphores with initial values, but since your threads never exit (they're infinite loops), you never get to the sem_unlink calls to delete those semaphores. If you kill the program (with ctrl-C or any other way), the semaphores will still exist in whatever state they are in. So if you run the program again, the sem_open calls will succeed (because you don't use O_EXCL), but they won't reset the semaphore value or state, so they might be in some odd state.
So you should make sure to call sem_unlink when the program STARTS, before calling sem_open. Better yet, don't use named semaphores at all -- use sem_init to initialize a couple of unnamed semaphores instead.