mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 18:31:55 -04:00
more aggressive dxgsg8/9 deferred loading
This commit is contained in:
parent
3b7f43058b
commit
823524e43c
@ -303,9 +303,9 @@ prepare_vertex_buffer(GeomVertexArrayData *data) {
|
|||||||
// Description: Updates the vertex buffer with the current data, and
|
// Description: Updates the vertex buffer with the current data, and
|
||||||
// makes it the current vertex buffer for rendering.
|
// makes it the current vertex buffer for rendering.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void DXGraphicsStateGuardian8::
|
bool DXGraphicsStateGuardian8::
|
||||||
apply_vertex_buffer(VertexBufferContext *vbc,
|
apply_vertex_buffer(VertexBufferContext *vbc,
|
||||||
const GeomVertexArrayDataHandle *reader) {
|
const GeomVertexArrayDataHandle *reader, bool force) {
|
||||||
DXVertexBufferContext8 *dvbc = DCAST(DXVertexBufferContext8, vbc);
|
DXVertexBufferContext8 *dvbc = DCAST(DXVertexBufferContext8, vbc);
|
||||||
|
|
||||||
if (dvbc->_vbuffer == NULL) {
|
if (dvbc->_vbuffer == NULL) {
|
||||||
@ -316,7 +316,9 @@ apply_vertex_buffer(VertexBufferContext *vbc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dvbc->_vbuffer != NULL) {
|
if (dvbc->_vbuffer != NULL) {
|
||||||
dvbc->upload_data(reader);
|
if (!dvbc->upload_data(reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
dvbc->mark_loaded(reader);
|
dvbc->mark_loaded(reader);
|
||||||
|
|
||||||
@ -338,7 +340,9 @@ apply_vertex_buffer(VertexBufferContext *vbc,
|
|||||||
dvbc->create_vbuffer(*_screen, reader);
|
dvbc->create_vbuffer(*_screen, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
dvbc->upload_data(reader);
|
if (!dvbc->upload_data(reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
dvbc->mark_loaded(reader);
|
dvbc->mark_loaded(reader);
|
||||||
_active_vbuffer = NULL;
|
_active_vbuffer = NULL;
|
||||||
}
|
}
|
||||||
@ -360,6 +364,7 @@ apply_vertex_buffer(VertexBufferContext *vbc,
|
|||||||
<< ") failed" << D3DERRORSTRING(hr);
|
<< ") failed" << D3DERRORSTRING(hr);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -401,9 +406,9 @@ prepare_index_buffer(GeomPrimitive *data) {
|
|||||||
// Description: Updates the index buffer with the current data, and
|
// Description: Updates the index buffer with the current data, and
|
||||||
// makes it the current index buffer for rendering.
|
// makes it the current index buffer for rendering.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void DXGraphicsStateGuardian8::
|
bool DXGraphicsStateGuardian8::
|
||||||
apply_index_buffer(IndexBufferContext *ibc,
|
apply_index_buffer(IndexBufferContext *ibc,
|
||||||
const GeomPrimitivePipelineReader *reader) {
|
const GeomPrimitivePipelineReader *reader, bool force) {
|
||||||
DXIndexBufferContext8 *dibc = DCAST(DXIndexBufferContext8, ibc);
|
DXIndexBufferContext8 *dibc = DCAST(DXIndexBufferContext8, ibc);
|
||||||
|
|
||||||
if (dibc->_ibuffer == NULL) {
|
if (dibc->_ibuffer == NULL) {
|
||||||
@ -411,7 +416,9 @@ apply_index_buffer(IndexBufferContext *ibc,
|
|||||||
dibc->create_ibuffer(*_screen, reader);
|
dibc->create_ibuffer(*_screen, reader);
|
||||||
|
|
||||||
if (dibc->_ibuffer != NULL) {
|
if (dibc->_ibuffer != NULL) {
|
||||||
dibc->upload_data(reader);
|
if (!dibc->upload_data(reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
dibc->mark_loaded(reader);
|
dibc->mark_loaded(reader);
|
||||||
|
|
||||||
_d3d_device->SetIndices(dibc->_ibuffer, 0);
|
_d3d_device->SetIndices(dibc->_ibuffer, 0);
|
||||||
@ -431,7 +438,9 @@ apply_index_buffer(IndexBufferContext *ibc,
|
|||||||
dibc->create_ibuffer(*_screen, reader);
|
dibc->create_ibuffer(*_screen, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
dibc->upload_data(reader);
|
if (!dibc->upload_data(reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
dibc->mark_loaded(reader);
|
dibc->mark_loaded(reader);
|
||||||
_active_ibuffer = NULL;
|
_active_ibuffer = NULL;
|
||||||
@ -443,6 +452,8 @@ apply_index_buffer(IndexBufferContext *ibc,
|
|||||||
dibc->set_active(true);
|
dibc->set_active(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -812,7 +823,9 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader,
|
|||||||
|
|
||||||
VertexBufferContext *vbc = ((GeomVertexArrayData *)(data->get_object()))->prepare_now(get_prepared_objects(), this);
|
VertexBufferContext *vbc = ((GeomVertexArrayData *)(data->get_object()))->prepare_now(get_prepared_objects(), this);
|
||||||
nassertr(vbc != (VertexBufferContext *)NULL, false);
|
nassertr(vbc != (VertexBufferContext *)NULL, false);
|
||||||
apply_vertex_buffer(vbc, data);
|
if (!apply_vertex_buffer(vbc, data, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const GeomVertexAnimationSpec &animation =
|
const GeomVertexAnimationSpec &animation =
|
||||||
data_reader->get_format()->get_animation();
|
data_reader->get_format()->get_animation();
|
||||||
@ -917,7 +930,9 @@ draw_triangles(const GeomPrimitivePipelineReader *reader, bool force) {
|
|||||||
// Indexed, vbuffers.
|
// Indexed, vbuffers.
|
||||||
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
||||||
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
||||||
apply_index_buffer(ibc, reader);
|
if (!apply_index_buffer(ibc, reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
_d3d_device->DrawIndexedPrimitive
|
_d3d_device->DrawIndexedPrimitive
|
||||||
(D3DPT_TRIANGLELIST,
|
(D3DPT_TRIANGLELIST,
|
||||||
@ -987,7 +1002,9 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) {
|
|||||||
// Indexed, vbuffers, one line triangle strip.
|
// Indexed, vbuffers, one line triangle strip.
|
||||||
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
||||||
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
||||||
apply_index_buffer(ibc, reader);
|
if (!apply_index_buffer(ibc, reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
_d3d_device->DrawIndexedPrimitive
|
_d3d_device->DrawIndexedPrimitive
|
||||||
(D3DPT_TRIANGLESTRIP,
|
(D3DPT_TRIANGLESTRIP,
|
||||||
@ -1055,7 +1072,9 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) {
|
|||||||
// Indexed, vbuffers, individual triangle strips.
|
// Indexed, vbuffers, individual triangle strips.
|
||||||
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
||||||
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
||||||
apply_index_buffer(ibc, reader);
|
if (!apply_index_buffer(ibc, reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int start = 0;
|
unsigned int start = 0;
|
||||||
for (size_t i = 0; i < ends.size(); i++) {
|
for (size_t i = 0; i < ends.size(); i++) {
|
||||||
@ -1165,7 +1184,9 @@ draw_trifans(const GeomPrimitivePipelineReader *reader, bool force) {
|
|||||||
// Indexed, vbuffers.
|
// Indexed, vbuffers.
|
||||||
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
||||||
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
||||||
apply_index_buffer(ibc, reader);
|
if (!apply_index_buffer(ibc, reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int start = 0;
|
unsigned int start = 0;
|
||||||
for (size_t i = 0; i < ends.size(); i++) {
|
for (size_t i = 0; i < ends.size(); i++) {
|
||||||
@ -1265,7 +1286,9 @@ draw_lines(const GeomPrimitivePipelineReader *reader, bool force) {
|
|||||||
// Indexed, vbuffers.
|
// Indexed, vbuffers.
|
||||||
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
||||||
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
||||||
apply_index_buffer(ibc, reader);
|
if (!apply_index_buffer(ibc, reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
_d3d_device->DrawIndexedPrimitive
|
_d3d_device->DrawIndexedPrimitive
|
||||||
(D3DPT_LINELIST,
|
(D3DPT_LINELIST,
|
||||||
|
@ -56,13 +56,13 @@ public:
|
|||||||
virtual void release_texture(TextureContext *tc);
|
virtual void release_texture(TextureContext *tc);
|
||||||
|
|
||||||
virtual VertexBufferContext *prepare_vertex_buffer(GeomVertexArrayData *data);
|
virtual VertexBufferContext *prepare_vertex_buffer(GeomVertexArrayData *data);
|
||||||
void apply_vertex_buffer(VertexBufferContext *vbc,
|
bool apply_vertex_buffer(VertexBufferContext *vbc,
|
||||||
const GeomVertexArrayDataHandle *reader);
|
const GeomVertexArrayDataHandle *reader, bool force);
|
||||||
virtual void release_vertex_buffer(VertexBufferContext *vbc);
|
virtual void release_vertex_buffer(VertexBufferContext *vbc);
|
||||||
|
|
||||||
virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
|
virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
|
||||||
void apply_index_buffer(IndexBufferContext *ibc,
|
bool apply_index_buffer(IndexBufferContext *ibc,
|
||||||
const GeomPrimitivePipelineReader *reader);
|
const GeomPrimitivePipelineReader *reader, bool force);
|
||||||
virtual void release_index_buffer(IndexBufferContext *ibc);
|
virtual void release_index_buffer(IndexBufferContext *ibc);
|
||||||
|
|
||||||
virtual PT(GeomMunger) make_geom_munger(const RenderState *state,
|
virtual PT(GeomMunger) make_geom_munger(const RenderState *state,
|
||||||
|
@ -104,15 +104,17 @@ create_ibuffer(DXScreenData &scrn,
|
|||||||
// Description: Copies the latest data from the client store to
|
// Description: Copies the latest data from the client store to
|
||||||
// DirectX.
|
// DirectX.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void DXIndexBufferContext8::
|
bool DXIndexBufferContext8::
|
||||||
upload_data(const GeomPrimitivePipelineReader *reader) {
|
upload_data(const GeomPrimitivePipelineReader *reader, bool force) {
|
||||||
nassertv(reader->get_object() == get_data());
|
nassertr(reader->get_object() == get_data(), false);
|
||||||
Thread *current_thread = reader->get_current_thread();
|
Thread *current_thread = reader->get_current_thread();
|
||||||
|
|
||||||
nassertv(_ibuffer != NULL);
|
nassertr(_ibuffer != NULL, false);
|
||||||
PStatTimer timer(GraphicsStateGuardian::_load_index_buffer_pcollector,
|
|
||||||
current_thread);
|
|
||||||
|
|
||||||
|
const unsigned char *data_pointer = reader->get_read_pointer(force);
|
||||||
|
if (data_pointer == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
int data_size = reader->get_data_size_bytes();
|
int data_size = reader->get_data_size_bytes();
|
||||||
|
|
||||||
if (dxgsg8_cat.is_spam()) {
|
if (dxgsg8_cat.is_spam()) {
|
||||||
@ -121,18 +123,22 @@ upload_data(const GeomPrimitivePipelineReader *reader) {
|
|||||||
<< " bytes into index buffer " << _ibuffer << "\n";
|
<< " bytes into index buffer " << _ibuffer << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PStatTimer timer(GraphicsStateGuardian::_load_index_buffer_pcollector,
|
||||||
|
current_thread);
|
||||||
|
|
||||||
BYTE *local_pointer;
|
BYTE *local_pointer;
|
||||||
// HRESULT hr = _ibuffer->Lock(0, data_size, &local_pointer, 0);
|
// HRESULT hr = _ibuffer->Lock(0, data_size, &local_pointer, 0);
|
||||||
HRESULT hr = _ibuffer->Lock(0, data_size, &local_pointer, D3DLOCK_DISCARD);
|
HRESULT hr = _ibuffer->Lock(0, data_size, &local_pointer, D3DLOCK_DISCARD);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
dxgsg8_cat.error()
|
dxgsg8_cat.error()
|
||||||
<< "IndexBuffer::Lock failed" << D3DERRORSTRING(hr);
|
<< "IndexBuffer::Lock failed" << D3DERRORSTRING(hr);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphicsStateGuardian::_data_transferred_pcollector.add_level(data_size);
|
GraphicsStateGuardian::_data_transferred_pcollector.add_level(data_size);
|
||||||
memcpy(local_pointer, reader->get_read_pointer(true), data_size);
|
memcpy(local_pointer, data_pointer, data_size);
|
||||||
|
|
||||||
_ibuffer->Unlock();
|
_ibuffer->Unlock();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ public:
|
|||||||
|
|
||||||
void create_ibuffer(DXScreenData &scrn,
|
void create_ibuffer(DXScreenData &scrn,
|
||||||
const GeomPrimitivePipelineReader *reader);
|
const GeomPrimitivePipelineReader *reader);
|
||||||
void upload_data(const GeomPrimitivePipelineReader *reader);
|
bool upload_data(const GeomPrimitivePipelineReader *reader, bool force);
|
||||||
|
|
||||||
IDirect3DIndexBuffer8 *_ibuffer;
|
IDirect3DIndexBuffer8 *_ibuffer;
|
||||||
|
|
||||||
|
@ -231,14 +231,16 @@ create_vbuffer(DXScreenData &scrn,
|
|||||||
// Description: Copies the latest data from the client store to
|
// Description: Copies the latest data from the client store to
|
||||||
// DirectX.
|
// DirectX.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void DXVertexBufferContext8::
|
bool DXVertexBufferContext8::
|
||||||
upload_data(const GeomVertexArrayDataHandle *reader) {
|
upload_data(const GeomVertexArrayDataHandle *reader, bool force) {
|
||||||
nassertv(reader->get_object() == get_data());
|
nassertr(reader->get_object() == get_data(), false);
|
||||||
nassertv(_vbuffer != NULL);
|
nassertr(_vbuffer != NULL, false);
|
||||||
Thread *current_thread = reader->get_current_thread();
|
Thread *current_thread = reader->get_current_thread();
|
||||||
PStatTimer timer(GraphicsStateGuardian::_load_vertex_buffer_pcollector,
|
|
||||||
current_thread);
|
|
||||||
|
|
||||||
|
const unsigned char *data_pointer = reader->get_read_pointer(force);
|
||||||
|
if (data_pointer == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
int data_size = reader->get_data_size_bytes();
|
int data_size = reader->get_data_size_bytes();
|
||||||
|
|
||||||
if (dxgsg8_cat.is_spam()) {
|
if (dxgsg8_cat.is_spam()) {
|
||||||
@ -247,18 +249,22 @@ upload_data(const GeomVertexArrayDataHandle *reader) {
|
|||||||
<< " bytes into vertex buffer " << _vbuffer << "\n";
|
<< " bytes into vertex buffer " << _vbuffer << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PStatTimer timer(GraphicsStateGuardian::_load_vertex_buffer_pcollector,
|
||||||
|
current_thread);
|
||||||
|
|
||||||
BYTE *local_pointer;
|
BYTE *local_pointer;
|
||||||
// HRESULT hr = _vbuffer->Lock(0, data_size, &local_pointer, 0);
|
// HRESULT hr = _vbuffer->Lock(0, data_size, &local_pointer, 0);
|
||||||
HRESULT hr = _vbuffer->Lock(0, data_size, &local_pointer, D3DLOCK_DISCARD);
|
HRESULT hr = _vbuffer->Lock(0, data_size, &local_pointer, D3DLOCK_DISCARD);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
dxgsg8_cat.error()
|
dxgsg8_cat.error()
|
||||||
<< "VertexBuffer::Lock failed" << D3DERRORSTRING(hr);
|
<< "VertexBuffer::Lock failed" << D3DERRORSTRING(hr);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphicsStateGuardian::_data_transferred_pcollector.add_level(data_size);
|
GraphicsStateGuardian::_data_transferred_pcollector.add_level(data_size);
|
||||||
memcpy(local_pointer, reader->get_read_pointer(true), data_size);
|
memcpy(local_pointer, data_pointer, data_size);
|
||||||
|
|
||||||
_vbuffer->Unlock();
|
_vbuffer->Unlock();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ public:
|
|||||||
|
|
||||||
void create_vbuffer(DXScreenData &scrn,
|
void create_vbuffer(DXScreenData &scrn,
|
||||||
const GeomVertexArrayDataHandle *reader);
|
const GeomVertexArrayDataHandle *reader);
|
||||||
void upload_data(const GeomVertexArrayDataHandle *reader);
|
bool upload_data(const GeomVertexArrayDataHandle *reader, bool force);
|
||||||
|
|
||||||
IDirect3DVertexBuffer8 *_vbuffer;
|
IDirect3DVertexBuffer8 *_vbuffer;
|
||||||
int _fvf;
|
int _fvf;
|
||||||
|
@ -62,6 +62,7 @@
|
|||||||
#include "pStatTimer.h"
|
#include "pStatTimer.h"
|
||||||
#include "pStatCollector.h"
|
#include "pStatCollector.h"
|
||||||
#include "wdxGraphicsBuffer9.h"
|
#include "wdxGraphicsBuffer9.h"
|
||||||
|
#include "config_pgraph.h"
|
||||||
#ifdef HAVE_CG
|
#ifdef HAVE_CG
|
||||||
#include "Cg/cgD3D9.h"
|
#include "Cg/cgD3D9.h"
|
||||||
#endif
|
#endif
|
||||||
@ -391,10 +392,10 @@ prepare_vertex_buffer(GeomVertexArrayData *data) {
|
|||||||
// Description: Updates the vertex buffer with the current data, and
|
// Description: Updates the vertex buffer with the current data, and
|
||||||
// makes it the current vertex buffer for rendering.
|
// makes it the current vertex buffer for rendering.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void DXGraphicsStateGuardian9::
|
bool DXGraphicsStateGuardian9::
|
||||||
apply_vertex_buffer(VertexBufferContext *vbc,
|
apply_vertex_buffer(VertexBufferContext *vbc,
|
||||||
CLP(ShaderContext) *shader_context,
|
CLP(ShaderContext) *shader_context,
|
||||||
const GeomVertexArrayDataHandle *reader) {
|
const GeomVertexArrayDataHandle *reader, bool force) {
|
||||||
DXVertexBufferContext9 *dvbc = DCAST(DXVertexBufferContext9, vbc);
|
DXVertexBufferContext9 *dvbc = DCAST(DXVertexBufferContext9, vbc);
|
||||||
|
|
||||||
DBG_SH3 dxgsg9_cat.debug ( ) << "apply_vertex_buffer\n"; DBG_E
|
DBG_SH3 dxgsg9_cat.debug ( ) << "apply_vertex_buffer\n"; DBG_E
|
||||||
@ -420,7 +421,9 @@ apply_vertex_buffer(VertexBufferContext *vbc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dvbc->_vbuffer != NULL) {
|
if (dvbc->_vbuffer != NULL) {
|
||||||
dvbc->upload_data(reader);
|
if (!dvbc->upload_data(reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
dvbc->mark_loaded(reader);
|
dvbc->mark_loaded(reader);
|
||||||
|
|
||||||
@ -438,7 +441,9 @@ apply_vertex_buffer(VertexBufferContext *vbc,
|
|||||||
dvbc->create_vbuffer(*_screen, reader);
|
dvbc->create_vbuffer(*_screen, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
dvbc->upload_data(reader);
|
if (!dvbc->upload_data(reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
dvbc->mark_loaded(reader);
|
dvbc->mark_loaded(reader);
|
||||||
_active_vbuffer = NULL;
|
_active_vbuffer = NULL;
|
||||||
@ -565,6 +570,8 @@ apply_vertex_buffer(VertexBufferContext *vbc,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -606,9 +613,9 @@ prepare_index_buffer(GeomPrimitive *data) {
|
|||||||
// Description: Updates the index buffer with the current data, and
|
// Description: Updates the index buffer with the current data, and
|
||||||
// makes it the current index buffer for rendering.
|
// makes it the current index buffer for rendering.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void DXGraphicsStateGuardian9::
|
bool DXGraphicsStateGuardian9::
|
||||||
apply_index_buffer(IndexBufferContext *ibc,
|
apply_index_buffer(IndexBufferContext *ibc,
|
||||||
const GeomPrimitivePipelineReader *reader) {
|
const GeomPrimitivePipelineReader *reader, bool force) {
|
||||||
DXIndexBufferContext9 *dibc = DCAST(DXIndexBufferContext9, ibc);
|
DXIndexBufferContext9 *dibc = DCAST(DXIndexBufferContext9, ibc);
|
||||||
|
|
||||||
if (_lru) {
|
if (_lru) {
|
||||||
@ -620,7 +627,9 @@ apply_index_buffer(IndexBufferContext *ibc,
|
|||||||
dibc->create_ibuffer(*_screen, reader);
|
dibc->create_ibuffer(*_screen, reader);
|
||||||
|
|
||||||
if (dibc->_ibuffer != NULL) {
|
if (dibc->_ibuffer != NULL) {
|
||||||
dibc->upload_data(reader);
|
if (!dibc->upload_data(reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
dibc->mark_loaded(reader);
|
dibc->mark_loaded(reader);
|
||||||
|
|
||||||
_d3d_device->SetIndices(dibc->_ibuffer);
|
_d3d_device->SetIndices(dibc->_ibuffer);
|
||||||
@ -640,7 +649,9 @@ apply_index_buffer(IndexBufferContext *ibc,
|
|||||||
dibc->create_ibuffer(*_screen, reader);
|
dibc->create_ibuffer(*_screen, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
dibc->upload_data(reader);
|
if (!dibc->upload_data(reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
dibc->mark_loaded(reader);
|
dibc->mark_loaded(reader);
|
||||||
_active_ibuffer = NULL;
|
_active_ibuffer = NULL;
|
||||||
@ -652,6 +663,8 @@ apply_index_buffer(IndexBufferContext *ibc,
|
|||||||
dibc->set_active(true);
|
dibc->set_active(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -1367,7 +1380,9 @@ vertex_element_array -> vertex_element_type_array;
|
|||||||
|
|
||||||
VertexBufferContext *vbc = ((GeomVertexArrayData *)(data->get_object()))->prepare_now(get_prepared_objects(), this);
|
VertexBufferContext *vbc = ((GeomVertexArrayData *)(data->get_object()))->prepare_now(get_prepared_objects(), this);
|
||||||
nassertr(vbc != (VertexBufferContext *)NULL, false);
|
nassertr(vbc != (VertexBufferContext *)NULL, false);
|
||||||
apply_vertex_buffer(vbc, _current_shader_context, data);
|
if (!apply_vertex_buffer(vbc, _current_shader_context, data, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const GeomVertexAnimationSpec &animation =
|
const GeomVertexAnimationSpec &animation =
|
||||||
data_reader->get_format()->get_animation();
|
data_reader->get_format()->get_animation();
|
||||||
@ -1475,7 +1490,9 @@ draw_triangles(const GeomPrimitivePipelineReader *reader, bool force) {
|
|||||||
// Indexed, vbuffers.
|
// Indexed, vbuffers.
|
||||||
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
||||||
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
||||||
apply_index_buffer(ibc, reader);
|
if (!apply_index_buffer(ibc, reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//DBG_SH2 dxgsg9_cat.debug ( ) << "DrawIndexedPrimitive \n"; DBG_E
|
//DBG_SH2 dxgsg9_cat.debug ( ) << "DrawIndexedPrimitive \n"; DBG_E
|
||||||
|
|
||||||
@ -1561,7 +1578,9 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) {
|
|||||||
// Indexed, vbuffers, one line triangle strip.
|
// Indexed, vbuffers, one line triangle strip.
|
||||||
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
||||||
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
||||||
apply_index_buffer(ibc, reader);
|
if (!apply_index_buffer(ibc, reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//dxgsg9_cat.error ( ) << "DrawIndexedPrimitive D3DPT_TRIANGLESTRIP VERTICES: " << reader->get_num_vertices ( ) << "\n";
|
//dxgsg9_cat.error ( ) << "DrawIndexedPrimitive D3DPT_TRIANGLESTRIP VERTICES: " << reader->get_num_vertices ( ) << "\n";
|
||||||
|
|
||||||
@ -1637,7 +1656,9 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) {
|
|||||||
// Indexed, vbuffers, individual triangle strips.
|
// Indexed, vbuffers, individual triangle strips.
|
||||||
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
||||||
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
||||||
apply_index_buffer(ibc, reader);
|
if (!apply_index_buffer(ibc, reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int start = 0;
|
unsigned int start = 0;
|
||||||
for (size_t i = 0; i < ends.size(); i++) {
|
for (size_t i = 0; i < ends.size(); i++) {
|
||||||
@ -1751,7 +1772,9 @@ draw_trifans(const GeomPrimitivePipelineReader *reader, bool force) {
|
|||||||
// Indexed, vbuffers.
|
// Indexed, vbuffers.
|
||||||
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
||||||
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
||||||
apply_index_buffer(ibc, reader);
|
if (!apply_index_buffer(ibc, reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int start = 0;
|
unsigned int start = 0;
|
||||||
for (size_t i = 0; i < ends.size(); i++) {
|
for (size_t i = 0; i < ends.size(); i++) {
|
||||||
@ -1851,7 +1874,9 @@ draw_lines(const GeomPrimitivePipelineReader *reader, bool force) {
|
|||||||
// Indexed, vbuffers.
|
// Indexed, vbuffers.
|
||||||
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
|
||||||
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
nassertr(ibc != (IndexBufferContext *)NULL, false);
|
||||||
apply_index_buffer(ibc, reader);
|
if (!apply_index_buffer(ibc, reader, force)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
_d3d_device->DrawIndexedPrimitive
|
_d3d_device->DrawIndexedPrimitive
|
||||||
(D3DPT_LINELIST,
|
(D3DPT_LINELIST,
|
||||||
@ -2349,10 +2374,23 @@ bool vertex_buffer_page_in_function (LruPage *lru_page)
|
|||||||
// allocate vertex buffer
|
// allocate vertex buffer
|
||||||
Thread *current_thread = Thread::get_current_thread();
|
Thread *current_thread = Thread::get_current_thread();
|
||||||
CPT(GeomVertexArrayDataHandle) reader = vertex_buffer->get_data()->get_handle(current_thread);
|
CPT(GeomVertexArrayDataHandle) reader = vertex_buffer->get_data()->get_handle(current_thread);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Not sure if this is the correct thing to do. Can we return false
|
||||||
|
from the page_in function? Will we get called again next frame if
|
||||||
|
we do?
|
||||||
|
if (allow_incomplete_render) {
|
||||||
|
// Check if the data is resident before continuing.
|
||||||
|
const unsigned char *data_pointer = reader->get_read_pointer(false);
|
||||||
|
if (data_pointer == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
vertex_buffer -> allocate_vbuffer (*(gsg->_screen), reader);
|
vertex_buffer -> allocate_vbuffer (*(gsg->_screen), reader);
|
||||||
|
|
||||||
// update vertex buffer
|
// update vertex buffer
|
||||||
vertex_buffer -> upload_data(reader);
|
vertex_buffer -> upload_data(reader, true);
|
||||||
vertex_buffer -> set_resident(true);
|
vertex_buffer -> set_resident(true);
|
||||||
|
|
||||||
if (DEBUG_LRU && dxgsg9_cat.is_debug())
|
if (DEBUG_LRU && dxgsg9_cat.is_debug())
|
||||||
@ -2390,10 +2428,24 @@ bool index_buffer_page_in_function (LruPage *lru_page)
|
|||||||
// allocate vertex buffer
|
// allocate vertex buffer
|
||||||
Thread *current_thread = Thread::get_current_thread();
|
Thread *current_thread = Thread::get_current_thread();
|
||||||
GeomPrimitivePipelineReader reader(index_buffer->get_data(), current_thread);
|
GeomPrimitivePipelineReader reader(index_buffer->get_data(), current_thread);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Not sure if this is the correct thing to do. Can we return false
|
||||||
|
from the page_in function? Will we get called again next frame if
|
||||||
|
we do?
|
||||||
|
if (allow_incomplete_render) {
|
||||||
|
// Check if the data is resident before continuing.
|
||||||
|
const unsigned char *data_pointer = reader.get_read_pointer(false);
|
||||||
|
if (data_pointer == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
index_buffer -> allocate_ibuffer (*(gsg->_screen), &reader);
|
index_buffer -> allocate_ibuffer (*(gsg->_screen), &reader);
|
||||||
|
|
||||||
// update vertex buffer
|
// update vertex buffer
|
||||||
index_buffer -> upload_data (&reader);
|
index_buffer -> upload_data (&reader, true);
|
||||||
index_buffer -> set_resident(true);
|
index_buffer -> set_resident(true);
|
||||||
|
|
||||||
if (DEBUG_LRU && dxgsg9_cat.is_debug())
|
if (DEBUG_LRU && dxgsg9_cat.is_debug())
|
||||||
|
@ -93,13 +93,13 @@ public:
|
|||||||
void release_shader(ShaderContext *sc);
|
void release_shader(ShaderContext *sc);
|
||||||
|
|
||||||
virtual VertexBufferContext *prepare_vertex_buffer(GeomVertexArrayData *data);
|
virtual VertexBufferContext *prepare_vertex_buffer(GeomVertexArrayData *data);
|
||||||
void apply_vertex_buffer(VertexBufferContext *vbc, CLP(ShaderContext) *shader_context,
|
bool apply_vertex_buffer(VertexBufferContext *vbc, CLP(ShaderContext) *shader_context,
|
||||||
const GeomVertexArrayDataHandle *reader);
|
const GeomVertexArrayDataHandle *reader, bool force);
|
||||||
virtual void release_vertex_buffer(VertexBufferContext *vbc);
|
virtual void release_vertex_buffer(VertexBufferContext *vbc);
|
||||||
|
|
||||||
virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
|
virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
|
||||||
void apply_index_buffer(IndexBufferContext *ibc,
|
bool apply_index_buffer(IndexBufferContext *ibc,
|
||||||
const GeomPrimitivePipelineReader *reader);
|
const GeomPrimitivePipelineReader *reader, bool force);
|
||||||
virtual void release_index_buffer(IndexBufferContext *ibc);
|
virtual void release_index_buffer(IndexBufferContext *ibc);
|
||||||
|
|
||||||
virtual void begin_occlusion_query();
|
virtual void begin_occlusion_query();
|
||||||
|
@ -202,15 +202,17 @@ create_ibuffer(DXScreenData &scrn,
|
|||||||
// Description: Copies the latest data from the client store to
|
// Description: Copies the latest data from the client store to
|
||||||
// DirectX.
|
// DirectX.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void DXIndexBufferContext9::
|
bool DXIndexBufferContext9::
|
||||||
upload_data(const GeomPrimitivePipelineReader *reader) {
|
upload_data(const GeomPrimitivePipelineReader *reader, bool force) {
|
||||||
nassertv(reader->get_object() == get_data());
|
nassertr(reader->get_object() == get_data(), false);
|
||||||
Thread *current_thread = reader->get_current_thread();
|
Thread *current_thread = reader->get_current_thread();
|
||||||
|
|
||||||
nassertv(_ibuffer != NULL);
|
nassertr(_ibuffer != NULL, false);
|
||||||
PStatTimer timer(GraphicsStateGuardian::_load_index_buffer_pcollector,
|
|
||||||
current_thread);
|
|
||||||
|
|
||||||
|
const unsigned char *data_pointer = reader->get_read_pointer(force);
|
||||||
|
if (data_pointer == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
int data_size = reader->get_data_size_bytes();
|
int data_size = reader->get_data_size_bytes();
|
||||||
|
|
||||||
if (dxgsg9_cat.is_spam()) {
|
if (dxgsg9_cat.is_spam()) {
|
||||||
@ -218,6 +220,8 @@ upload_data(const GeomPrimitivePipelineReader *reader) {
|
|||||||
<< "copying " << data_size
|
<< "copying " << data_size
|
||||||
<< " bytes into index buffer " << _ibuffer << "\n";
|
<< " bytes into index buffer " << _ibuffer << "\n";
|
||||||
}
|
}
|
||||||
|
PStatTimer timer(GraphicsStateGuardian::_load_index_buffer_pcollector,
|
||||||
|
current_thread);
|
||||||
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
BYTE *local_pointer;
|
BYTE *local_pointer;
|
||||||
@ -233,11 +237,12 @@ upload_data(const GeomPrimitivePipelineReader *reader) {
|
|||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
dxgsg9_cat.error()
|
dxgsg9_cat.error()
|
||||||
<< "IndexBuffer::Lock failed" << D3DERRORSTRING(hr);
|
<< "IndexBuffer::Lock failed" << D3DERRORSTRING(hr);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphicsStateGuardian::_data_transferred_pcollector.add_level(data_size);
|
GraphicsStateGuardian::_data_transferred_pcollector.add_level(data_size);
|
||||||
memcpy(local_pointer, reader->get_read_pointer(true), data_size);
|
memcpy(local_pointer, data_pointer, data_size);
|
||||||
|
|
||||||
_ibuffer->Unlock();
|
_ibuffer->Unlock();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ public:
|
|||||||
void free_ibuffer();
|
void free_ibuffer();
|
||||||
void allocate_ibuffer(DXScreenData &scrn, const GeomPrimitivePipelineReader *reader);
|
void allocate_ibuffer(DXScreenData &scrn, const GeomPrimitivePipelineReader *reader);
|
||||||
void create_ibuffer(DXScreenData &scrn, const GeomPrimitivePipelineReader *reader);
|
void create_ibuffer(DXScreenData &scrn, const GeomPrimitivePipelineReader *reader);
|
||||||
void upload_data(const GeomPrimitivePipelineReader *reader);
|
bool upload_data(const GeomPrimitivePipelineReader *reader, bool force);
|
||||||
|
|
||||||
IDirect3DIndexBuffer9 *_ibuffer;
|
IDirect3DIndexBuffer9 *_ibuffer;
|
||||||
int _managed;
|
int _managed;
|
||||||
|
@ -441,14 +441,16 @@ create_vbuffer(DXScreenData &scrn,
|
|||||||
// Description: Copies the latest data from the client store to
|
// Description: Copies the latest data from the client store to
|
||||||
// DirectX.
|
// DirectX.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void DXVertexBufferContext9::
|
bool DXVertexBufferContext9::
|
||||||
upload_data(const GeomVertexArrayDataHandle *reader) {
|
upload_data(const GeomVertexArrayDataHandle *reader, bool force) {
|
||||||
nassertv(reader->get_object() == get_data());
|
nassertr(reader->get_object() == get_data(), false);
|
||||||
nassertv(_vbuffer != NULL);
|
nassertr(_vbuffer != NULL, false);
|
||||||
Thread *current_thread = reader->get_current_thread();
|
Thread *current_thread = reader->get_current_thread();
|
||||||
PStatTimer timer(GraphicsStateGuardian::_load_vertex_buffer_pcollector,
|
|
||||||
current_thread);
|
|
||||||
|
|
||||||
|
const unsigned char *data_pointer = reader->get_read_pointer(force);
|
||||||
|
if (data_pointer == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
int data_size = reader->get_data_size_bytes();
|
int data_size = reader->get_data_size_bytes();
|
||||||
|
|
||||||
if (dxgsg9_cat.is_spam()) {
|
if (dxgsg9_cat.is_spam()) {
|
||||||
@ -456,6 +458,8 @@ upload_data(const GeomVertexArrayDataHandle *reader) {
|
|||||||
<< "copying " << data_size
|
<< "copying " << data_size
|
||||||
<< " bytes into vertex buffer " << _vbuffer << "\n";
|
<< " bytes into vertex buffer " << _vbuffer << "\n";
|
||||||
}
|
}
|
||||||
|
PStatTimer timer(GraphicsStateGuardian::_load_vertex_buffer_pcollector,
|
||||||
|
current_thread);
|
||||||
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
BYTE *local_pointer;
|
BYTE *local_pointer;
|
||||||
@ -469,11 +473,12 @@ upload_data(const GeomVertexArrayDataHandle *reader) {
|
|||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
dxgsg9_cat.error()
|
dxgsg9_cat.error()
|
||||||
<< "VertexBuffer::Lock failed" << D3DERRORSTRING(hr);
|
<< "VertexBuffer::Lock failed" << D3DERRORSTRING(hr);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphicsStateGuardian::_data_transferred_pcollector.add_level(data_size);
|
GraphicsStateGuardian::_data_transferred_pcollector.add_level(data_size);
|
||||||
memcpy(local_pointer, reader->get_read_pointer(true), data_size);
|
memcpy(local_pointer, data_pointer, data_size);
|
||||||
|
|
||||||
_vbuffer->Unlock();
|
_vbuffer->Unlock();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ public:
|
|||||||
void free_vbuffer();
|
void free_vbuffer();
|
||||||
void allocate_vbuffer(DXScreenData &scrn, const GeomVertexArrayDataHandle *reader);
|
void allocate_vbuffer(DXScreenData &scrn, const GeomVertexArrayDataHandle *reader);
|
||||||
void create_vbuffer(DXScreenData &scrn, const GeomVertexArrayDataHandle *reader);
|
void create_vbuffer(DXScreenData &scrn, const GeomVertexArrayDataHandle *reader);
|
||||||
void upload_data(const GeomVertexArrayDataHandle *reader);
|
bool upload_data(const GeomVertexArrayDataHandle *reader, bool force);
|
||||||
|
|
||||||
IDirect3DVertexBuffer9 *_vbuffer;
|
IDirect3DVertexBuffer9 *_vbuffer;
|
||||||
int _fvf;
|
int _fvf;
|
||||||
|
@ -69,7 +69,7 @@ extern ConfigVariableBool m_dual_flash;
|
|||||||
|
|
||||||
extern ConfigVariableList load_file_type;
|
extern ConfigVariableList load_file_type;
|
||||||
extern ConfigVariableString default_model_extension;
|
extern ConfigVariableString default_model_extension;
|
||||||
extern ConfigVariableBool allow_incomplete_render;
|
extern EXPCL_PANDA ConfigVariableBool allow_incomplete_render;
|
||||||
|
|
||||||
extern EXPCL_PANDA void init_libpgraph();
|
extern EXPCL_PANDA void init_libpgraph();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user